Signal

Error-stack engine

Identifies the host JavaScript engine from the shape of a throw-away Error and from engine-specific Error-instance properties. One of several independent engine-reveal vectors that feed the consistency layer.

Reviewed

Tier 1 engine

What it measures

The signal throws a single synthetic Error and inspects properties of the resulting instance to classify the host JavaScript engine family. The classification result is engine-bound: Chrome and Safari running on the same machine produce different engine identifications because the underlying engines expose different non-standard Error API surfaces.

This is one of several independent engine-reveal vectors in the library. Each vector probes a structurally different surface, so a spoofer that patches one vector still surfaces through the others. An anti-detect tool would have to coordinate all of them simultaneously to evade detection.

The result feeds an engine-vs-UA cross-check in the consistency layer. A spoofer claiming a browser whose engine identification does not match the underlying engine surfaces produces a flag without breaking the user-visible page.

How it's collected

The collector performs a small synchronous probe — no DOM, no network, no async — and folds a small number of boolean / categorical reads into a stable hash. The exact properties probed, the priority order in which they are evaluated, the engine-label vocabulary, and the hash-input composition are part of the internal recipe and not part of the public contract.

The probe is sub-millisecond and safe under strict Content Security Policy environments. The probe contents do not depend on file paths, line numbers, or page URLs, so the hash is stable across call-site contexts.

Confidence rules

ConfidenceTrigger
normalClassifier returned an engine label, including the catch-all label for unrecognised engines. The boolean property reads alone carry entropy.
absentThe outer try/catch fired (for example, the Error constructor was replaced with one that itself throws); sentinel: 'threw'.

Why engine-bound

The Error-instance shape is emitted by the JavaScript engine at throw time, not by hardware or the operating system. Different browsers shipped by different engine teams expose different non-standard properties on the Error object and produce a different stack-string shape. Including the signal in the hardware-only fingerprint would shatter cross-browser stability — a visitor switching browsers on the same machine would produce a different hardware fingerprint even though the device is unchanged.

Because the variation is purely engine-driven, the hash is stable within an engine family across browser versions, page loads, and pages on the same origin. That is the defining property of an engine-bound signal.

Things worth knowing

  • Hash inputs are stack-content-independent. File paths, line numbers, message text, and other call-site-specific data do not enter the hash; only properties of the Error instance and a small set of derived booleans do.
  • The signal has no 'degraded' or 'stabilized' confidence state. It is either 'normal' or 'absent'.
  • On iOS, every browser engine is WebKit-based regardless of the UA-string brand. The cross-check in the consistency layer accounts for this so that, for example, Chrome-on-iOS is not flagged as a spoofer for producing the WebKit engine identification.
  • DevTools injection and strict CSP environments do not affect the probe.

Last reviewed 2026-06-04