What are invisible characters?
Unicode code points that occupy space in the byte stream but render as nothing, or as something indistinguishable from an ordinary space. They survive copy and paste, they are not visible in any normal editor, and most of them have legitimate typographic uses that AI output does not need.
The full list
| Character | Code point | Renders as | What it breaks |
|---|---|---|---|
| Zero width space | U+200B | nothing | String equality, search, database keys |
| Zero width non-joiner | U+200C | nothing | Git diffs on visually identical lines |
| Zero width joiner | U+200D | nothing | Same, and stripping it carelessly corrupts emoji |
| Soft hyphen | U+00AD | nothing until a line wraps | Survives proofreading, then appears in print |
| Word joiner | U+2060 | nothing | Word boundary logic in search indexes |
| Non-breaking space | U+00A0 | a space | CSV parsing, trim(), shell arguments |
| Byte order mark | U+FEFF | nothing | Breaks JSON parsing when it lands at the start of a file |
| Left-to-right mark | U+200E | nothing | Unexpected reordering in mixed-script text |
Alongside those, AI text carries visible characters that behave like artefacts: em dash U+2014, en dash U+2013, curly quotes U+201C U+201D U+2018 U+2019, and the ellipsis U+2026. They are not invisible, but they break code strings and shell commands the same way, and the em dash carries a stylistic signal all of its own.
What each one actually costs you
Git. Two lines that look identical but differ by one zero width character are different bytes. You get a diff nobody can explain, a merge conflict with no visible cause, and a blame history that points at the wrong commit.
Databases. A trailing U+200B on a key makes lookups fail silently. The value is there, the query returns nothing, and the two strings look the same in every log you check.
Data pipelines. A non-breaking space passes visual inspection and then breaks the CSV column split, or survives a trim() that was supposed to clean it.
Search. A word joiner inside a term prevents the tokeniser seeing the word, so the document never matches.
Publishing. Soft hyphens are invisible until the line wraps at a different width, which is usually after the piece is published.
How to find them yourself
Do not take any tool’s word for it, including this one. In a browser console:
[...text].filter(c => /[- ]/.test(c))
.map(c => 'U+' + c.codePointAt(0).toString(16).toUpperCase().padStart(4,'0'))
That returns the code points present in a string. On the command line, cat -A or hexdump -C shows the bytes directly. In VS Code, the unicode-highlight setting flags them in the gutter.
If a tool tells you it found something and this returns an empty array, the tool is wrong.
Why these are not the Claude watermark
Because Anthropic’s mark is statistical. It biases which tokens the model picks, using a secret key, and there is no character carrying it. A watermark made of the code points above would show up under hexdump and be removed by one regular expression, which is why nobody would build one that way.
Tools that describe these characters as “the watermark” are describing a real thing and mislabelling it. Delete them because they break your build, not because you think it changes what a detector sees. The full explanation.
Who this is not for
If nothing in your pipeline touches bytes, this probably does not matter to you. Prose that only ever gets read by humans is unaffected by a zero width space, and cleaning it changes nothing you can perceive.
The people who need this are developers, data engineers, anyone maintaining a CMS, and anyone who has spent an hour on a diff that made no sense.