Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why can reduce start throwing after a marker-only string is cleaned?

Our marker-only string becomes an empty array after cleanup. reduce without an initial value then throws TypeError; the reduction seeded with an empty string returns empty text. Choose an explicit empty-input policy before aggregation.

Tested October 24, 2026 · v24.22.1 · 4 measured runs

Open the Claude text cleaner · Full measured data

Code-point comparison for Marker only

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
Marker only
direct string / keep / invisible true
"​"
U+200B
""
Removed 1 invisible character
{"elements":["​"],"without":"​","withoutCalls":0,"withInitial":"​","withCalls":1}
{"elements":[],"without":{"error":"TypeError"},"withoutCalls":0,"withInitial":"","withCalls":0}
One letter
direct string / keep / invisible true
"A"
U+0041
"A"
No selected invisible characters found
{"elements":["A"],"without":"A","withoutCalls":0,"withInitial":"A","withCalls":1}
{"elements":["A"],"without":"A","withoutCalls":0,"withInitial":"A","withCalls":1}
Two letters
direct string / keep / invisible true
"AB"
U+0041 U+0042
"AB"
No selected invisible characters found
{"elements":["A","B"],"without":"AB","withoutCalls":1,"withInitial":"AB","withCalls":2}
{"elements":["A","B"],"without":"AB","withoutCalls":1,"withInitial":"AB","withCalls":2}
Empty text
direct string / keep / invisible true
""
""
No selected invisible characters found
{"elements":[],"without":{"error":"TypeError"},"withoutCalls":0,"withInitial":"","withCalls":0}
{"elements":[],"without":{"error":"TypeError"},"withoutCalls":0,"withInitial":"","withCalls":0}

Deletion changes the input length before reduction begins

The marker-only source initially spreads into an array containing one element. Its unseeded reduction returns that element without invoking the callback. After cleanup deletes the marker, spreading produces no elements and the same unseeded call throws TypeError. The seeded branch returns an empty string with zero callback calls. Recording both arrays and callback counts shows that the exception is an accumulator initialization issue; no concatenation callback ran on the cleaned empty input. The cleaner itself completed its selected deletion successfully.

A single element can hide the missing initialization

The one-letter A reference also returns its element without an unseeded callback call. The two-letter AB reference invokes that callback once, while the seeded version invokes its callback twice. Thus a callback counter of zero is not by itself a failure indicator: it occurs for a successful single-element reduction and the failing empty reduction. The explicit empty-text reference reproduces the post-cleanup error without any invisible character. Pairing outcomes with element count prevents a caller from diagnosing the absence of callback activity as a cleaner malfunction.

An initial value is part of the aggregation contract

For this concatenation example, the empty string is the chosen initial result. Other operations need their own identity or an explicit rejection of missing input. Supplying a seed solely to suppress an exception can change result type or meaning when the intended accumulator is different. Our observer uses code-point string iteration and concatenation only; it does not sum numbers, merge records or inspect sparse arrays. The earlier join guide asks how structures are represented; this page isolates how reduce obtains its first accumulator and whether it can start on empty content.

Preserve the empty result as an observable state

A source consisting entirely of selected characters is a useful fixture whenever downstream processing expects at least one element. Keep the original text, cleaned text and destination exception separately in logs. Our generated evidence contains no real user payload, and the minimal DOM tests exercise the saved homepage callback rather than a browser file picker. Neither successful concatenation nor an empty result identifies Claude authorship or measures a statistical watermark. Validate the actual destination contract before treating an exception as evidence that more cleanup is needed.

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 Array.prototype.reduce 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.