Skip to main content

Guides

Fix “SyntaxError: Invalid Character” in Python

CleanPastedText editorial · Updated

Text workbench

Try it on your text

Cleaning mode

Removes hidden characters and standardizes AI-style punctuation.

Try a real example

Each sample contains a problem you cannot see.

Original

Pasted text

0 chars · 0 words

Cleaned

Ready to copy

0 changes

Text stays in this browser

Same words · No AI rewriting · No content logging

Why does Python say my code has an invalid character?

Because an invisible character rode along with a copy-paste and landed somewhere outside a string or comment — a blank space, an identifier, a line of operators — where Python's tokenizer expects a real token and finds a Unicode code point it does not recognize as one. The code looks completely normal in most editors, because the character causing the error has no visible glyph.

Since Python 3.9, the fix for finding it got much easier. Before bpo-40593, CPython's tokenizer just reported a generic invalid syntax at the position. That patch, merged for the 3.9 release, made the tokenizer name the exact code point in the message and put a caret directly under it — turning a guessing game into a one-line diagnosis.

What the error looks like, verified character by character

The examples below were produced by actually running CPython 3.11.15 against a one-line script for each character — not copied from a forum post. Each script assigns a value next to the character in question, outside any string:

CharacterExact SyntaxError text
Zero width space (U+200B)invalid non-printable character U+200B
Byte order mark (U+FEFF)invalid non-printable character U+FEFF
Non-breaking space (U+00A0)invalid non-printable character U+00A0
Narrow no-break space (U+202F)invalid non-printable character U+202F
Curly left quote (U+2018)invalid character '‘' (U+2018)

The last row is the tell for the underlying rule: a code point with no glyph gets the non-printable phrasing, since there's nothing to display next to it; a code point that does render something, like a smart quote, gets shown inside the message itself. Both come from the same check and both name the code point, which is what makes them fixable without guessing.

Why do some invisible characters crash and others don't?

Python only rejects these characters where a real token is expected — outside string literals and comments. Inside a string, the exact same character is accepted silently:

>>> x = "hello\u200bworld"
>>> len(x)
11
>>> x == "helloworld"
False

No error, no warning — just a string that is one character longer than it looks and fails an equality check that should obviously pass. This is the case worth worrying about more than the SyntaxError: a crash tells you exactly where to look, but a zero-width space sitting inside a string constant, a dictionary key, or a value read from a config file changes program behavior with no diagnostic at all. It's the same class of problem covered in more depth by the guide to sanitizing copied text for code, JSON, and CSV.

How do I fix “SyntaxError: invalid character” right now?

  1. Read the code point out of the message. Python's traceback already did the hard part — it names the character (U+200B, U+00A0, etc.) and shows a caret under its exact position in the offending line.
  2. Delete just that character if it's a single stray instance — most editors let you position the cursor right after the caret and press backspace once, even though you cannot see what you're deleting.
  3. Run the whole file through a character-level cleaner if the error keeps recurring on different lines, which usually means the source you copied from (a PDF, a slide deck, a chat interface) injected the character repeatedly rather than once. Paste the code above and check the report — it lists every hidden character by name and code point before you copy the cleaned version back.
  4. Re-save the file as plain UTF-8 if a BOM (U+FEFF) was the cause and it keeps reappearing — some editors re-add a BOM on every save unless the encoding is explicitly set to “UTF-8” rather than “UTF-8 with BOM.”

Where these characters actually come from

Almost never a keyboard. The characters in the table above enter Python files through copy-paste from sources that use them for real typesetting reasons that don't survive the trip into plain text: PDFs and justified word processors insert non-breaking and narrow no-break spaces to control line breaks, web pages and Word documents use zero-width spaces as word-break hints inside long tokens like URLs, and files saved by older Windows tools carry a byte order mark by default. AI chat interfaces add their own version of the same problem — see how hidden characters end up in ChatGPT output for what that specific pipeline does. None of it is malicious or unusual; it's just formatting metadata that plain text was never meant to carry, and the invisible character remover strips it in one pass without touching a single visible character, entirely in your browser.

Common questions

Frequently asked questions

What does “SyntaxError: invalid character” mean in Python?

It means the tokenizer hit a Unicode code point it does not accept outside a string or comment — usually an invisible character such as a zero-width space (U+200B), byte order mark (U+FEFF), or non-breaking space (U+00A0) that arrived through copy-paste. Since Python 3.9 (bpo-40593), the message names the exact code point: invalid non-printable character U+200B for characters with no glyph, or invalid character '…' (U+2018) for lookalikes like curly quotes that do render something.

Why didn't my editor show the character before Python complained?

Because characters like U+200B, U+FEFF, and U+2060 render nothing — that is their entire purpose. A code editor without a “render control characters” setting draws the exact same glyphs whether the invisible character is there or not, so the line looks identical either way. Python's tokenizer reads code point by code point rather than pixel by pixel, so it is the first thing in the pipeline that actually notices.

Can an invisible character break Python code without raising an error?

Yes, and this is the more dangerous case. Python's syntax check only rejects invisible characters where a real token is expected — outside strings and comments. Inside a string literal, the same character is silently accepted as part of the value: "hello\u200bworld" parses fine and is 11 characters long, not 10, with no warning at all. That is how invisible characters cause failed string comparisons, broken email addresses, or mismatched dictionary keys instead of a crash.

Which invisible characters trigger this error, and which don't?

Tested directly against CPython 3.11: a zero-width space (U+200B), byte order mark (U+FEFF), non-breaking space (U+00A0), and narrow no-break space (U+202F) each produced invalid non-printable character U+XXXX when placed outside a string. A character with a visible glyph that Python still rejects as an operator, like a curly left quote (U+2018), instead produced invalid character '‘' (U+2018) — the same underlying check, phrased differently because there is something to show. Characters Python treats as valid identifier parts, such as many combining marks and non-Latin letters, do not trigger it at all.

How do I stop this from happening when I copy code from ChatGPT or Claude?

Paste the snippet through a character-level cleaner before saving it, rather than retyping the whole line by hand. AI chat interfaces render code blocks as HTML, and HTML copy paths are exactly where non-breaking spaces and narrow no-break spaces get introduced in place of ordinary ones — see how hidden characters end up in ChatGPT output for the underlying mechanism. A cleaner set to a code-safe preset removes the invisible characters and leaves every visible character, including indentation and non-ASCII identifiers, untouched.

Continue reading

Related guides & tools