Text

The Invisible Characters Breaking Your Text

A space that is not a space, a character with no width, a mark at the start of a file. Where they come from and how to get them out.

An invisible character is a real character that renders as blank, or as nothing at all. The usual suspects are U+00A0, the non-breaking space; U+200B, the zero-width space; U+FEFF, the byte order mark; and the eleven typographic spaces between U+2000 and U+200A. Your eyes skip them. Every string comparison a computer makes counts them. That is why a spreadsheet lookup fails on a name you can plainly read, why a pasted password is rejected, and why two lines that look identical refuse to collapse into one.

The symptom always has the same shape: two things that look the same are not the same. Nothing is wrong with the software, or with your eyes. There is an extra character in there and it has no picture.

Which characters are invisible?

Two groups, with different causes.

Spaces that are not the space bar

Unicode's space separator category holds 17 characters. Exactly one of them is the key on your keyboard. The other 16 look like a space, measure like a space, and fail to match a space.

Characters with no width at all

These do not even look like a space. They occupy zero pixels, so text containing one is visually identical to text without it.

How do I see invisible characters?

You cannot, directly. You detect them three ways.

Count them. Paste the text into the whitespace cleaner here and it reports what it found before it changes anything: how many non-breaking spaces, how many other Unicode spaces, how many zero-width characters, how many lines end in a space. An empty report rules out the whole category in ten seconds.

Compare the length with what you expect. A product code that should be 8 characters and reports 9 has a passenger.

Turn on your editor's highlighting. VS Code boxes in invisible and ambiguous characters by default, a feature it added after the Trojan Source disclosure in 2021 showed that source code can be made to read one way to a human and another to a compiler. Most editors have a version of this, under "show whitespace" or "render control characters".

Why does find and replace not remove them?

Because the shortcuts you would reach for disagree about what counts as whitespace.

In JavaScript, \s and trim() both match U+00A0 and U+FEFF but not U+200B, which is classified as a format character rather than a space. Python behaves the same way: '\xa0'.isspace() is true, '\u200b'.isspace() is false, so .strip() leaves it behind. Excel is worse. TRIM removes only the ASCII space, character 32, and CLEAN only characters 0 to 31, so neither touches the non-breaking space at 160. There you need SUBSTITUTE(A1,CHAR(160)," ") before TRIM does anything useful.

To target them explicitly, use a regex. Tick Regular expression in the find and replace tool and search for [\u00A0\u200B\u200C\u200D\u2060\uFEFF] — the zero-width set plus the non-breaking space. Read the match count before you replace anything.

Where do they come from?

Almost always a copy and paste across a boundary: a web page, where every   copies out as a real U+00A0; a Word document, where autoformatting adds them around numbers and initials; a PDF, which carries whatever spacing the typesetter used.

The byte order mark is the exception, and it breaks parsers rather than comparisons. Excel's UTF-8 CSV export puts one at the front of the file. JSON.parse throws on a leading U+FEFF, since JSON allows only space, tab, carriage return and line feed as whitespace. Python's csv module hands you a first field called \ufeffid unless you open the file with the utf-8-sig encoding, which exists for exactly this. If a CSV conversion produces one key with a name you cannot see anything wrong with, check that first.

How do I strip them out?

For prose, replace every exotic space with an ordinary one and delete the zero-width characters outright. That is the default in the cleaner: "Replace exotic spaces" covers all 16 non-standard space separators, "Remove invisible characters" covers the zero-width set and the byte order mark, and the before and after counts tell you how many left.

For code, turn off the line trimming and the space collapsing first. Indentation is syntax in Python, YAML and Makefiles, and two trailing spaces are a hard line break in Markdown. Removing the zero-width characters stays safe in all of those cases, and it is the option you actually need — a zero-width space pasted into a source file produces an error nobody can see by reading.

For lists, clean before you deduplicate rather than after: two entries that differ by one trailing non-breaking space are two different strings to every deduplication routine ever written. Removing duplicate lines from a list covers the other half of that problem, which is deciding whether case and word order count as a difference.

When should you leave them alone?

A blanket strip is wrong at least as often as it is right.

That is the honest limit of every tool in this category: it finds characters, it cannot read intent.

What a whitespace cleaner will not catch

Several invisible-character problems look identical from the outside and need a different fix:

All four have the same signature as an invisible space, and none of them respond to the same treatment. Working out which one you have is most of the job.

The whitespace cleaner lists what it found by type before it touches anything, which is the part worth using: knowing that the text contains four non-breaking spaces and one byte order mark tells you more than a cleaned version with no explanation. It runs in the page, so nothing you paste is sent anywhere.

If you got here because a search would not find a word that is visibly in the document, the invisible character is only one of the reasons that happens. Find and replace: the mistakes that cost you an hour goes through the others, including curly quotes and the two-way swap that eats both words.

Frequently asked questions

What are invisible characters in text?

They are Unicode characters that render as blank or as nothing at all, such as the non-breaking space (U+00A0), the zero-width space (U+200B) and the byte order mark (U+FEFF). They take up a position in the string and count towards its length, so a computer treats text containing one as different from text without it, even though the two look identical.

How do I find invisible characters in a string?

Paste it into a tool that counts them by type, or compare the character count against the length you expect. Most code editors can also highlight them: VS Code boxes in invisible and ambiguous characters by default. A plain visual inspection will never work, which is the whole problem.

Why does my search not find a word that is clearly there?

Usually because one of the spaces in it is not an ordinary space. A non-breaking space is pixel-identical to a normal one and does not match it, so searching for "New York" fails on text that contains "New" plus U+00A0 plus "York". Curly quotes copied from Word cause the same silent failure.

Why does TRIM not remove the spaces in my spreadsheet?

Excel’s TRIM only removes character 32, the ASCII space, and CLEAN only removes characters 0 to 31. The non-breaking space is character 160, so neither function touches it. Wrap the cell in SUBSTITUTE(A1,CHAR(160)," ") first, then TRIM will work as expected.

Is it safe to remove all zero-width characters?

Not always. The zero-width joiner holds emoji sequences together, so stripping it turns one family emoji into several separate people. The zero-width non-joiner is grammatically meaningful in Persian and Arabic script. On plain English prose, removing them is safe and usually the right call.

What is a byte order mark and why is it breaking my file?

It is U+FEFF, three bytes at the start of a UTF-8 file that announce the encoding. Many parsers do not expect it: JSON.parse throws on a leading one, and a CSV reader will report the first column header as an unrecognised name. Python solves it with the utf-8-sig encoding, which strips the mark while reading.

Last updated September 19, 2026