Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why does replaceAll throw even when its regex finds no text?

In all four controls, replaceAll with /AB/ throws TypeError, including empty and unmatched strings. The /AB/g and literal AB versions succeed. Cleanup restores matching tokens but cannot repair a regex argument without the global flag.

Tested October 17, 2026 · v24.19.0 · 4 measured runs

Open the Claude text cleaner · Full measured data

Code-point comparison for Marked repeated token

Measured inputs and outputs

These are locally constructed test strings, not evidence that Claude inserts these characters. We executed the saved homepage script snapshot with a minimal DOM harness and compared exact strings. The invisible-checkbox state and dash mode appear in each row. The observations below test this page's specific question. Destination observations run independently on the exact input and cleaned strings in the recorded Node runtime. We make no detector-score or statistical-watermark removal claim.

Before and after observations; full code points and strings are in the JSON download.
Fixture and modeInput string and code pointsOutput and cleaner statusObserved before / after
Marked repeated token
direct string / keep / invisible true
"A​BA​B"
U+0041 U+200B U+0042 U+0041 U+200B U+0042
"ABAB"
Removed 2 invisible characters
{"nonGlobal":{"errorName":"TypeError"},"literal":"A​BA​B","global":"A​BA​B"}
{"nonGlobal":{"errorName":"TypeError"},"literal":"XX","global":"XX"}
Repeated control
direct string / keep / invisible true
"ABAB"
U+0041 U+0042 U+0041 U+0042
"ABAB"
No selected invisible characters found
{"nonGlobal":{"errorName":"TypeError"},"literal":"XX","global":"XX"}
{"nonGlobal":{"errorName":"TypeError"},"literal":"XX","global":"XX"}
Absent token
direct string / keep / invisible true
"XY"
U+0058 U+0059
"XY"
No selected invisible characters found
{"nonGlobal":{"errorName":"TypeError"},"literal":"XY","global":"XY"}
{"nonGlobal":{"errorName":"TypeError"},"literal":"XY","global":"XY"}
Empty source
direct string / keep / invisible true
""
""
No selected invisible characters found
{"nonGlobal":{"errorName":"TypeError"},"literal":"","global":""}
{"nonGlobal":{"errorName":"TypeError"},"literal":"","global":""}

The exception survives both successful and absent matches

We call replaceAll three ways on each supplied string: with a nonglobal ordinary regex, a string search token, and a global ordinary regex. The first call throws TypeError for every fixture. That includes XY, which has no AB token, and the empty source, where no characters could match. The marked repeated-token string initially has U+200B interrupting both AB occurrences. After the cleaner removes two code points it becomes ABAB, yet the nonglobal call still throws. The literal and global calls now both produce XX. The exception and the broken token are independent conditions, so repairing the input alone cannot make the first API call succeed.

Literal search and regex search have different argument contracts

The string token AB works with replaceAll without a regex flag because it is not a regular-expression argument. Our global regex supplies g explicitly and produces the same output for these simple literal letters. This equivalence is limited to this pattern and these controls; it does not imply that arbitrary strings and regex patterns mean the same thing. Existing pages discuss regex state, escaping and replacement-dollar semantics. This page keeps the replacement fixed at X, uses no captures or special replacement tokens, and asks only why an ordinary regex is rejected before useful replacement occurs. Custom Symbol.replace objects and modified regex flag properties are outside the experiment.

Fix the search argument separately from the source text

When replacing every literal occurrence, decide whether a string token is the intended operation. When a regular expression is required, construct it with the flags and semantics appropriate to that operation, including the global requirement measured here. Retain a control with no matches so argument-validation errors cannot be confused with a failure to find text. Avoid catching the exception and silently returning an unchanged string as if replacement succeeded. The saved observer records error names independently from the two successful outputs. It demonstrates a concrete programming boundary around cleaned text, while leaving broader pattern design, case rules and replacement formatting to their own separately tested contracts.

Reproduce this test

Save reproduce.cjs and tested-app.js in the same folder. Run the command below with Node.js. The harness prints its runtime, script SHA-256 and every measured row. Compare those rows with the original record. Using a newer script or runtime creates a new experiment; retain the version information with your rerun.

node reproduce.cjs

Reference and next check

ECMAScript String.prototype.replaceAll provides the relevant primary definition. The table and fixture analysis are original measurements. For broader inspection, use our Unicode inspector. Read the scope distinction before interpreting cleanup as a watermark result.