A broken monitoring dashboard is relatively easy to catch. A dashboard showing believable but incorrect numbers is much more dangerous.That is one of the traps with WebRTC's getStats() API.In my work on real-time communication systems, I have used WebRTC statistics to investigate connection quality, recovery behavior, and production reliability. One recurring problem is that a metric can be collected correctly yet still be misinterpreted.The API exposes detailed information about RTP streams, packet loss, jitter, candidate pairs, codecs, frame processing, round-trip time, and more. But those values do not all behave the same way. Some are cumulative counters. Some are instantaneous measurements. Some depend on information reported by the remote endpoint. Some disappear when the monitored object changes.If those distinctions are ignored, the calculations can still produce perfectly reasonable-looking numbers.A bitrate graph keeps moving. Packet loss stays near zero. Jitter looks excellent. Nothing crashes.The numbers are simply answering a different question from the one you thought you asked.Here are six mistakes worth checking before trusting a WebRTC monitoring pipeline.1. Most Values Are Counters, Not RatesValues such as bytesReceived, packetsReceived, framesDecoded, and nackCount are cumulative counters in the WebRTC Statistics model.Reading bytesReceived once therefore does not tell you the current bitrate. It tells you how many bytes have accumulated for that stats object.To calculate a rate, compare two observations:const bitrate = ((curr.bytesReceived - prev.bytesReceived) * 8) / ((curr.timestamp - prev.timestamp) / 1000);The subtraction is straightforward. The details around it are where monitoring implementations often go wrong.First, use the timestamp associated with the stats object rather than assuming the interval between two calls to your polling function is the measurement interval. A polling loop might execute every second, but that does not mean the underlying measurements represented by two reports are exactly one second apart.Second, make sure curr and prev describe the same stats object.That leads to the next problem.2. The Object You're Measuring Can Change Underneath YouSuppose a monitoring implementation finds the selected ICE candidate pair in every report and calculates deltas between successive observations.That works until the selected pair changes.An ICE restart, for example, can result in candidate-pair objects disappearing and new ones appearing. The replacement object has its own identity and its own counters. The WebRTC Stats specification defines stable identifiers for monitored objects; the important point for a monitoring pipeline is not to treat a replacement object as the continuation of the previous one.If the monitoring code treats the new object as though it were the next observation of the old one, subtracting the counters can produce nonsense such as a large negative delta.Match objects by their stats id:const prevStat = prevReport.get(curr.id);if (!prevStat) { // No baseline exists for this object yet. // Store it and wait for another sample. return null;}A stats ID identifies a particular monitored object. The mistake is not that an object's ID randomly changes; the object itself can change.When a previously observed object disappears or a new one appears, treat that as an object transition rather than interpolating across it.3. Calling getStats() Faster Doesn't Guarantee Fresher MeasurementsIt is tempting to assume that calling getStats() more frequently gives you proportionally finer-grained measurements.That assumption is unsafe.The WebRTC Stats specification allows implementations to use caching or throttling and does not give applications control over the sampling cadence of every underlying measurement. Two reports obtained from separate calls can therefore contain stats whose timestamps have not advanced.If you calculate a rate from those observations, your measurement interval may be zero:const deltaMs = curr.timestamp - prev.timestamp;if (deltaMs