Zerowidth CleanerAll measured guides

CLAUDE WATERMARK REMOVER · PRACTICAL TEST

Why does JSON.stringify throw after BigInt conversion succeeds?

Our cleaned 123 converts to BigInt, then ordinary JSON.stringify throws TypeError. An explicit decimal-string field serializes successfully, with a different JSON type. Cleanup and exact integer conversion do not choose a transport schema.

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

Open the Claude text cleaner · Full measured data

Code-point comparison for Marked integer

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 integer
direct string / keep / invisible true
"12​3"
U+0031 U+0032 U+200B U+0033
"123"
Removed 1 invisible character
{"conversion":false,"errorName":"SyntaxError"}
{"conversion":true,"digits":"123","json":null,"errorName":"TypeError","decimalStringJson":"{\"value\":\"123\"}"}
Plain integer
direct string / keep / invisible true
"123"
U+0031 U+0032 U+0033
"123"
No selected invisible characters found
{"conversion":true,"digits":"123","json":null,"errorName":"TypeError","decimalStringJson":"{\"value\":\"123\"}"}
{"conversion":true,"digits":"123","json":null,"errorName":"TypeError","decimalStringJson":"{\"value\":\"123\"}"}
Large integer
direct string / keep / invisible true
"9007199254740993"
U+0039 U+0030 U+0030 U+0037 U+0031 U+0039 U+0039 U+0032 U+0035 U+0034 U+0037 U+0034 U+0030 U+0039 U+0039 U+0033
"9007199254740993"
No selected invisible characters found
{"conversion":true,"digits":"9007199254740993","json":null,"errorName":"TypeError","decimalStringJson":"{\"value\":\"9007199254740993\"}"}
{"conversion":true,"digits":"9007199254740993","json":null,"errorName":"TypeError","decimalStringJson":"{\"value\":\"9007199254740993\"}"}
Fraction control
direct string / keep / invisible true
"12.5"
U+0031 U+0032 U+002E U+0035
"12.5"
No selected invisible characters found
{"conversion":false,"errorName":"SyntaxError"}
{"conversion":false,"errorName":"SyntaxError"}

Successful conversion moves the failure to another stage

The marked integer fails BigInt construction with SyntaxError until its U+200B is removed. After cleanup the digits are 123 and construction succeeds, but serializing an object containing that BigInt throws TypeError. The plain small integer and large 9007199254740993 control both reach the same export failure. That confirms this is not restricted to values beyond Number’s safe range. The fraction control continues to fail construction and never reaches the serializer. We also serialize each successfully constructed integer as an explicitly authored decimal-string field. That export succeeds and keeps the exact recorded digits. Its quotes identify a JSON string, not an unquoted JSON numeric value.

Precision and transport acceptance are separate contracts

The existing large-integer guide compares Number rounding with BigInt exactness and explicitly excludes transport testing. This experiment adds the next export operation. It neither installs a prototype hook nor supplies a replacer, so ordinary behavior remains visible. The observer catches an exception to record its name; catching it is not a successful export. Its null json field denotes our missing-result placeholder, not a serialized null payload. The evidence itself contains decimal labels rather than raw BigInts so the measurement file can be written safely. We do not test raw JSON extensions, server parsers, database columns or a custom reviver that reconstructs a BigInt from a string.

Agree on representation before sending exact integers

If a destination accepts decimal strings, specify that type and define the syntax and conversion rules for the receiver. A string export is one measured representation here, not a transparent replacement for every numeric schema. Converting through Number merely to avoid the serializer error can lose exactness for large integers, as the earlier independent guide demonstrates. Retain the original digits and distinguish a construction failure from an export failure in diagnostics. Our tests do not patch global objects or choose an application protocol. The attached reproducer performs both exports locally and makes every exception stage explicit, so a future integration can compare its actual transport behavior with these limited controls.

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 JSON parsing and serialization 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.