Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why does decodeURIComponent still throw after cleaning?

Deleting U+200B repairs our broken %41 spelling. Incomplete %4 and invalid UTF-8 %FF still throw URIError; encoded %E2%80%8B decodes to a marker only after the cleanup stage.

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

Open the Claude text cleaner · Full measured data

Code-point comparison for Marked percent escape

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 percent escape
direct string / keep / invisible true
"%4​1"
U+0025 U+0034 U+200B U+0031
"%41"
Removed 1 invisible character
{"accepted":false,"errorName":"URIError"}
{"accepted":true,"decoded":"A"}
Incomplete escape
direct string / keep / invisible true
"%4"
U+0025 U+0034
"%4"
No selected invisible characters found
{"accepted":false,"errorName":"URIError"}
{"accepted":false,"errorName":"URIError"}
Invalid UTF8 escape
direct string / keep / invisible true
"%FF"
U+0025 U+0046 U+0046
"%FF"
No selected invisible characters found
{"accepted":false,"errorName":"URIError"}
{"accepted":false,"errorName":"URIError"}
Valid encoded marker
direct string / keep / invisible true
"%E2%80%8B"
U+0025 U+0045 U+0032 U+0025 U+0038 U+0030 U+0025 U+0038 U+0042
"%E2%80%8B"
No selected invisible characters found
{"accepted":true,"decoded":"​"}
{"accepted":true,"decoded":"​"}

One removable interruption and two unchanged errors

Our first string places U+200B between the four and one of a percent escape. decodeURIComponent rejects that original spelling. The saved cleaner deletes the actual marker and leaves %41, which the observer decodes to A. The incomplete %4 control has no selected character to remove and continues to throw. The %FF control also stays unchanged and throws because its escaped byte does not form valid UTF-8. The complete encoded marker is accepted before and after cleaning, but its decoded result contains U+200B. Recording accepted status separately from decoded text exposes both exceptions and successful decoding of a character absent from the raw input.

Decoder validation is distinct from URL navigation

The destination operation is a direct decodeURIComponent call on a small component string. It does not construct a URL, consult a server, or test a browser address bar. Our earlier percent-path guide measures a valid encoded path surviving cleanup; this page focuses on strict decoder failures. The URI encoder guide starts with unmatched surrogates and tests the opposite conversion direction. We keep those questions separate. The cleaner neither decodes percent notation nor adds missing digits, and our observer catches exceptions only to record their names. That logging is not an application recovery policy and does not turn a rejected component into a usable destination.

Choose the component and the decoding stage deliberately

Retain the raw component, inspect its complete escape sequences, and decide whether a literal interruption is accidental before editing it. If decoding succeeds, inspect the decoded representation separately; the encoded-marker row shows why scanning only the ASCII notation misses a later character. Avoid decoding a whole URL as an undifferentiated string when escaped delimiters carry structural meaning. The fixtures do not test recursive decoding, legacy encodings, form plus rules or application-specific permissive parsers. A valid decoded A establishes only this local transformation. It provides no evidence about routing success, content provenance or removal of a model-level statistical watermark.

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 decodeURIComponent 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.