every realm speaks the same error dialect
Check id error-dialect-realm-agreement
What an ordinary browser yields
Every reachable realm returns a byte-identical signature — the same eight messages, the same stack dialect, the same location for the stack property.
What a detector infers
An engine's error messages are a dialect hiding in plain sight. Ask V8 to read a property of null and it says "Cannot read properties of null (reading 'x')"; SpiderMonkey says "x is null"; JavaScriptCore says "null is not an object (evaluating 'x')". None of that is specified — it is wording compiled into the binary. The check evaluates one shared source string in the page, in a fresh iframe realm and in a worker, provoking eight deliberate throws across eight error classes (reading a property of null, of undefined, calling a non-function, reducing an empty array, toFixed(999), Array(-1), JSON.parse('{'), decodeURIComponent('%')), and adds two structural facts: the stack's shape classified only as AT-style or at-sign style, and where the `stack` property lives (an own accessor, an own data property, or on the prototype). One engine serves all three realms, so all three must answer identically. DataDome is provably harvesting exactly this: one of its fields base64-decodes to "WorkerCaughtErr: Err: TypeError: Cannot read properties of null (reading 'getExtension')" — it builds an OffscreenCanvas inside a Worker, gets a null context back, calls getExtension on it, and ships the resulting message. The failure is the payload; it is not recovering from that error, it is collecting it, from the realm most tooling forgets. Two design choices are worth stating because they are what keep the check honest. It never names an engine, a version or a phrase, so there is no table to go stale — an engine nobody has ever seen simply agrees with itself and passes. And all three realms must evaluate the identical source bytes, because every engine quotes your own source text back inside these messages: if the page evaluated an inlined copy while the worker evaluated a string, our own minifier would rename the variables in one and not the other, and Firefox and Safari would then genuinely report different text for the same operation, failing a completely stock browser.
How to resolve it
Error messages are produced by the engine, and the engine is shared by the page, its iframes and its workers. Anything rewriting them in one realm cannot reach the others, because a fresh realm is constructed straight from the binary. If a stack or a message must be hidden, note that it has to be hidden identically in every realm at once — which is considerably harder than leaving it alone.
Read in the wild by
DataDome
Provokes a null-context TypeError INSIDE a Worker — it builds an OffscreenCanvas, gets a null WebGL context back, calls getExtension on it, and ships String(e) verbatim. The failure is the payload: it is not recovering from that error, it is harvesting the engine's exact message phrasing from the realm most tooling forgets.
payload field mUMHqX, base64 -> "WorkerCaughtErr: Err: TypeError: Cannot read properties of null (reading 'getExtension')"; main-thread counterpart aTJPJW = "Error\nat na (https://geo.captcha-delivery.com/captcha/?..."
glizzykingdreko/datadome-encryption — a real 232-field captured payloadAkamai
Lists error-message format as an explicit signal in its JavaScript-environment category, on the stated grounds that the wording differs by engine.
analysis/signal_categories.md, section 6 "JavaScript Environment": "Error messages — Format of error messages (differs by engine)"
Edioff/akamai-analysis — Bot Manager v2 signal inventoryEvery attribution traces to a published artifact. See the sources and their limits.
Other checks in Error subsystem
- the stack notices how deep the call chain actually was
error-stack-depth-fidelityDataDome's bytecode VM does something small and sharp: new Error(), read .stack, fold a hash over the text — we can read the exact opcodes… - the structured and string renderings of one call chain name the same frames
structured-stack-vs-string-stackChrome describes one call chain twice. Once as the human-readable string on Error.stack, and once as structured CallSite objects, through a… - a stack trace names the function it was thrown in
error-stack-livenessA stack trace is not a string, it is a measurement of the present moment — a function of who called whom, from what URL, at what column…
Clearcote is a browser built for fingerprint coherence
It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.
This audit takes no position on how Clearcote scores on Error subsystem checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.
See the other checks in Error subsystem — the family error-dialect-realm-agreement belongs to.