Build1 distinct publisher3 min readPublished
A month of translator recordings against Chrome 151 says the removeChild NotFoundError in your tracker is a documented replace-and-detach, gated on the element entering the viewport. That gating is why it hides.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
Chrome's translator re-scans what becomes visible rather than watching your document through a MutationObserver [16]. That one design choice decides what your instrumentation can see. The dev.to writeup behind these figures records about a month of measurement across Chrome, Edge, Firefox, Yandex and Google's translate_a/element.js widget, with the published numbers taken from a raw recording on Windows against Chrome 151, dated 2026-09-02 [3]. The author had written down, and had a probe proving, that Chrome never repairs a text node you restore. The probe sat below the fold. With the element in view, Chrome repaired the restored node in 210ms, and the shipped claim was wrong for two days [17]. A probe below the fold measures a translator that is not looking.
Whether you get a stack trace or a stuck screen is decided by markup most reviewers treat as cosmetic. A bare `{visible && 'There are 4 lights!'}` is cleared by React with `textContent`, and nothing throws; put a `<span>` next to it and the string becomes a real text node that React removes by reference, which is the throw [8]. Interpolation with literal text on both sides, `<p>There are {count} lights!</p>`, compiles to three separate text nodes, and that is the shape that freezes instead of crashing [9].
Treat the timings as claims about one app on one machine. The update series was five replicates of four updates sampled every 50ms [19], so a 210ms repair resolves into roughly four samples [1]. That is enough to argue hundreds of milliseconds, not enough to argue 210 against 180. For the numbers to transfer, your readers need to be on Chrome desktop with the node in view, and your update cadence needs to sit near the once-a-second counter used in the Edge and Firefox probes, which left the node connected and let writes land, reaching 6 in both browsers [12]. At that cadence a 210ms repair window leaves about a fifth of each second showing untranslated text [2], which is why a price that changes once is a flash nobody reports while a counter pays it every tick [20].
The second recovery option, writing your new value into the wrapper the translator already built [18], is where locale walks in. Splicing a fresh digit into an already-translated Dutch sentence works. In Russian it breaks agreement: `Intl.PluralRules('ru')` puts 4 in `few` and 7 in `many`, the noun follows the category, and an early build shipped exactly that error [21]. The merge now refuses when the plural category, the digit count or the sentence shape changes, or when the locale is unknown, and on refusal the reader gets the right number in the source language [22]. Dutch and German report `other` at every count, so a team developing against those locales never sees a refusal happen [23].
Two pieces of this are good engineering worth copying. Yandex wraps translated text in `<ya-tr-span>` and keeps your source string in a `data-value` attribute [6], which is a recovery path handed to you; Google's nested `<font dir="auto" style="vertical-align: inherit;">` pair keeps nothing [5]. And the fingerprint that actually works is the inline style, not the computed one: `getComputedStyle(wrapper).verticalAlign` returns `baseline` because the cascade resolved it, while `wrapper.style.verticalAlign` still reads `inherit` [14].
Ranked by verification strength, evidence, and original report placement.
The reported error is: Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
There is no local repro; affected sessions have nothing in common until you notice they had page translation on.
The author spent about a month measuring what translators do to a live DOM, covering Chrome, Edge, Firefox, Yandex and Google's translate_a/element.js widget; every figure comes from a raw recording taken 2026-09-02 on Windows against Chrome 151.
Chrome does not edit the existing text node: it builds a new one, wraps it, puts the wrapper where the original was, and detaches the original.
On a page translated to Dutch, 'There are 4 lights!' was replaced by a nested pair of <font dir="auto" style="vertical-align: inherit;"> elements containing 'Er zijn 4 lampen!'.
Yandex performs the same replacement using <ya-tr-span> and keeps the source string in a data-value attribute.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · September 4, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
Three ways to ask who embedded your iframe, and only one the host cannot switch off1 distinct publisher
build
The fourth Web Push requirement: iOS will not deliver until the user installs your site1 distinct publisher
build
Teaching the HTML parser to attach shadow roots retires the empty-then-pop flash1 distinct publisher
leadership
EU General Court leaves Edge outside the choice-screen duty Opera sued to trigger1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
One instrumented month, no second pair of eyes
The instrumentation is unusually legible for a blog post: a raw recording behind every figure, a Playwright spec readers can re-run, a no-signal control in the trigger experiment, five replicates for the timing numbers. The strongest signal is a retraction — the author had recorded that Chrome never repairs a restored node, found the probe had only ever run below the fold, and published the correction. What is missing is anyone else's machine: a single Windows box, Chrome 151, one recording date, and a mechanism conclusion resting on nine negative results over ten seconds.
Universal exposure, unmeasured uptake
Two adoption stories share one post and they point opposite ways. The behaviour causing the failure is shipped to everybody — four engines plus Google's embeddable widget — and React's 2018 won't-fix means the exposure has stood untouched for years. The response side is nearly empty: a package published the same week with no installs, no dependents and no deployment reported anywhere, plus a lint plugin the author did not write. Nothing in this reporting measures how many real sessions arrive with translation switched on, which is the number that would turn exposure into incidence.
Headline oversells what the body keeps narrowing
The title promises an unmounted app; the writing then spends most of its length taking that back — Edge and Firefox largely fine, the patch everyone copies made things worse rather than better, the merge refusal admitted as a real loss to readers, the whole refusal path invisible in the two languages the work was developed against, and two figures publicly corrected. Where it under-reports itself is the silent half: writes that vanish into a detached node throw nothing, so the crash count anyone observes is a floor and the true reach is larger than the visible evidence.
The diagnosis arrives with the author's own fix attached
The developer describing the failure is also the one shipping the remedy, on a platform where nothing stands between draft and publish. That belongs on the record. Pulling the other way: the JSON behind each figure and the spec that produced it are in the repo, the demo is split across two documents precisely so the control is honest, the package is described as doing nothing on Edge and Firefox because nothing is correct there, and a competing tool by someone else is recommended without hedging.
Firm on mechanism, thin on reach
I would bet on the mechanism and hold back on the magnitude. Replace-and-detach and the two failing JSX shapes are checkable in an afternoon, and a 2018 issue thread describes the same shape independently of this author. How much of your traffic this touches, whether Chrome 152 behaves like Chrome 151, how much of the viewport-gating conclusion survives a second test rig, and where Safari — never mentioned — sits are all open questions this reporting does not close.