A monitoring bug kept fast interactions out of INP for years

Summary

SpeedCurve RUM users are about to see INP move for reasons that have nothing to do with their own code. A wrapper bug in the lux.js library kept the request for zero-duration reporting from reaching the browser, and the release note says soft navigations were most affected.

Record which lux.js version was live over each reporting period before comparing anything. An INP change dated to the upgrade is a measurement change, not a regression.

lux.js, the real user monitoring script from SpeedCurve, a web performance monitoring vendor, has been asking browsers to report every interaction no matter how brief since May 2024, and browsers have been ignoring the request. Harry Roberts, an independent web performance consultant, found the cause and wrote it up: the library’s own wrapper around PerformanceObserver put the extra observer options inside a nested options property instead of passing them at the top level. The durationThreshold: 0 never reached the browser, which fell back to its default of 104 milliseconds. Anything faster was not delivered through the Event Timing observer at all. Joseph Wynn at SpeedCurve opened a patch a little over an hour after the report, and the fix was merged on 24 September. Version 4.5.3, carrying that fix, was published on 30 September.

Roberts writes that the missing interactions could change both the INP distribution and the attribution SpeedCurve calculated, and the post stops there rather than predicting which way numbers will move. Downward is the likelier direction. The interactions that went missing were all fast ones, and a page view where every interaction finished under the threshold would have produced no INP at all, so the fix brings fast page views into a distribution that had been leaving them out. SpeedCurve’s public release note says the correction mostly affected soft navigations, the in-page route changes a single-page app makes without a full reload, so the shift is likely to be smaller on sites built from ordinary page loads.

The risk is in how the change gets read, not in the measurement itself. An INP distribution that moves during the week of 30 September, on a site that shipped no application code, invites a hunt for a regression that does not exist. It is the same trap as a rank tracker quietly losing data, where the number falls and the site is fine.

Roberts’ post addresses SpeedCurve users and does not discuss other data sources. The bug lived in lux.js, so CrUX, Search Console’s Core Web Vitals report, and any other RUM vendor were measuring on their own terms throughout. Search Console holding steady while SpeedCurve moves is the expected result after the upgrade, not a contradiction to reconcile.

What to do

Treat the upgrade date as a break in the series and work around it:

  • Record which lux.js version served each RUM period, and annotate the date 4.5.3 went live. Any before-and-after comparison that crosses an unlabelled version change is worthless.
  • Compare the full INP distribution across the upgrade, not the 75th percentile on its own, and split soft navigations from full page loads if the tooling allows. A new cluster of fast page views is the fix working, not a performance win to claim.
  • Check the library version before opening an investigation into an INP change dated to late September or October.
  • If lux.js is self-hosted, the fix lands when the file is updated, not on 30 September.
  • Pause INP alerting and any performance budget check that straddles the upgrade until a clean post-fix period exists to baseline against.