FontFace rejects bytes that are not a font, with a DOMException
Every load of the garbage bytes rejects, and every rejection is a DOMException (SyntaxError on every browser measured; NetworkError is also accepted). N/A where the FontFace API is not exposed or no load settles within three seconds (severity: warn).
What a detector infers
The CSS Font Loading API lets a page build a font face from bytes it already holds: new FontFace(family, buffer).load(). What happens to bytes that are not a font is fixed by the specification, not by the platform: the promise rejects with a SyntaxError DOMException and the face's status becomes "error". The family name plays no part in that; it is only the name the face would be registered under.
So this row hands the loader 256 bytes that parse as no font format, under a random family name and under four common system names, and reads two things: whether any of those loads succeeded, and whether every rejection was a DOMException.
Measured on 2026-09-24, Chrome, Edge and Brave 153 and Firefox 156 rejected every one with a SyntaxError DOMException, and Firefox did so in a private window, with resistFingerprinting, with fingerprinting protection on all targets, with page fonts or downloadable fonts switched off, with a system-font allowlist and at the lowest font-visibility level. Playwright's Firefox 144 did the same. Camoufox gates this path on the family name instead.
Under a name on its persona's font allowlist, such as Arial, the garbage is reported loaded without being parsed (an empty buffer, a 404 url() and a missing local() face also "load"). Under any other name the load is refused with NS_ERROR_FAILURE, an internal Gecko error code that genuine Firefox never passes to a page. The same gate refuses real fonts under names it does not know, which is why ordinary web fonts misbehave in it.
How to resolve it
Leave the web-font path alone. A font allowlist belongs on system-font lookup, meaning local() and the fonts a page can enumerate, not on faces the page supplies itself. A patched loader has to keep both specified outcomes: bytes that are not a font reject with a SyntaxError DOMException, and a real font loads under any family name.
Read in the wild by
Camoufox (measured)
Measured while establishing ground truth for this row, from a page script in the main world. Every persona tried either reports the garbage as a loaded font under Arial or leaks NS_ERROR_FAILURE under the random name, and all but one do both.
Camoufox 152.0.4-beta.30 (python package cloverlabs-camoufox 0.6.0): default, os=macos and NewContext macOS and Linux personas load the garbage under Arial (status "loaded") and reject the random name with NS_ERROR_FAILURE ([object Exception], not a DOMException). os=linux rejects both names with NS_ERROR_FAILURE. A 153.2-based fork behaves like the default. Twelve genuine configurations on the same machine: SyntaxError DOMException under both names.
camoufox.comCamoufox issues
Traces FontFace status and loading through a single family-name helper, IsFontAllowed in FontFaceImpl.h, which consults the persona's font allowlist. The issue reads the local() half of that gate; this row reads what the same helper does to bytes the page supplies itself.
daijro/camoufox#759Every attribution traces to a published artifact. See the sources and their limits.
Other checks in Error subsystem
- every realm speaks the same error dialect
error-dialect-realm-agreementAn engine's error messages are a dialect hiding in plain sight. - the stack notices how deep the call chain actually was
error-stack-depth-fidelityA commercial detector's bytecode VM does something small and sharp: new Error(), read .stack, fold a hash over the text — we can read the… - 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… - stack serialisation matches the engine actually measured
error-stack-format-vs-engineCaptures of three real browsers on one machine pin this precisely: for the same collector, the serialised stack begins with the error name… - surfaces that must fail, fail in the specified way
collection-outcome-shapeA deployed agent records a status code beside every value it collects, and its own generated documentation maps each code to the places that… - DOM exception wording matches the engine actually measured
dom-exception-wording-vs-engineBuried inside a deployed commercial agent's DataTransfer collector is a test that a caught TypeError's message contains a particular phrase…
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 fontface-rejects-invalid-font-data belongs to.
