JSON String Escaping Rules: What Your API Parser Rejects and Why
A debugging-first guide to JSON string escaping — which characters must be escaped per RFC 8259, how to read the error your parser throws, and real payload examples that show the fix.
JSON String Escaping Rules: What Your API Parser Rejects and Why
When an API returns SyntaxError: Unexpected token or a webhook handler throws invalid JSON, the problem is almost always an unescaped character inside a string. The fix takes two minutes once you know the rule — but hunting down which character triggered the error without a reference wastes hours.
This guide walks through the complete set of JSON string escaping rules defined by RFC 8259, pairs each rule with the parser error it prevents, and shows real before/after payload examples. By the end you will be able to read any JSON parse error and trace it back to exactly the character that caused it.
The Eight Characters JSON Requires You to Escape
RFC 8259 — the JSON specification — mandates escaping for eight character values inside any JSON string. These are the only ones that must be escaped; everything else is optional.
| Character | Code point | Escape | What breaks without it | |---|---|---|---| | Quotation mark | U+0022 | \" | Closes the string early | | Reverse solidus | U+005C | \\ | Starts an invalid escape | | Solidus | U+002F | \/ | Optional everywhere except <script> blocks | | Backspace | U+0008 | \b | Produces an invisible control byte | | Form feed | U+000C | \f | Produces an invisible control byte | | Line feed | U+000A | \n | Breaks the string across lines | | Carriage return | U+000D | \r | Breaks the string across lines | | Horizontal tab | U+0009 | \t | Produces an ambiguous whitespace byte |
Additionally, every character below U+0020 (the ASCII space) is a control character and must be escaped using a \uXXXX sequence — even if it has no named shorthand. In practice this means byte values 0x00–0x1F cannot appear literally in a JSON string.
The solidus (/) is a persistent source of confusion. Escaping it as \/ is optional everywhere except when you embed JSON directly inside an HTML <script> tag, where the literal sequence </ would close the tag prematurely. Most production serializers skip the solidus escape to keep payloads shorter; parsers must accept both forms.
Reading the Error: What Each Parser Message Means
I have spent more time than I care to admit staring at JSON parse errors in API logs. The messages vary by language and runtime, but they map to a short list of causes.
SyntaxError: Unterminated string constant (JavaScript, browsers) An unescaped " appeared inside a string value. The parser thought the string ended there and then found unexpected tokens after the closing position.
// BROKEN — the unescaped " closes the string at "error"
{"message": "She said "hello" to the server"}
// FIXED
{"message": "She said \"hello\" to the server"}
SyntaxError: Invalid \escape or Invalid escape character A \ is present that is not followed by one of the eight recognized sequences (", \, /, b, f, n, r, t) or a u followed by four hex digits. The most common cause is a Windows file path.
// BROKEN — backslashes are escape-prefix characters
{"path": "C:\Users\alice\report.pdf"}
// FIXED — every backslash doubled
{"path": "C:\\Users\\alice\\report.pdf"}
json.decoder.JSONDecodeError: Invalid control character at line 1 (Python) A literal control character — most often a raw newline (0x0A) or a tab (0x09) — is present inside a string. This happens frequently when developers interpolate multi-line text into a JSON template without serializing it first.
// BROKEN — literal newline in the string
{"note": "line one
line two"}
// FIXED
{"note": "line one\nline two"}
unexpected character U+0000 A null byte (0x00) is inside the string. This turns up when binary data is included in a text field — for example when someone base64-decodes a value in the wrong place and writes the raw bytes into JSON.
Real Payload: Before and After
Here is a realistic webhook body that contains three independent escaping mistakes, all in one payload:
Before (broken — three errors)
{
"user": "O'Brien",
"path": "C:\Reports\Q2\final.csv",
"note": "Review on 07/01:
compare with Q1."
}
Mistake 1: the apostrophe in O'Brien is not a JSON special character — it is fine and does not need escaping. Apostrophes are safe to include literally. Many developers escape them out of habit from SQL or HTML; that habit is wrong here.
Mistake 2: C:\Reports\Q2\final.csv contains three unescaped backslashes. Any JSON parser will interpret \R, \Q, and \f as escape sequences — \f happens to be a valid form feed, so this one silently mangles the path. The others throw Invalid escape character.
Mistake 3: The literal newline inside note is a control character (U+000A) that is not permitted in JSON strings.
After (valid)
{
"user": "O'Brien",
"path": "C:\\Reports\\Q2\\final.csv",
"note": "Review on 07/01: compare with Q1."
}
Note that I left the apostrophe untouched, doubled every backslash, and collapsed the multi-line note to a single line. The payload is now valid JSON that every compliant parser accepts.
You can paste either version into the JSON String Escape and Unescape Tool and see the escaped output immediately without installing anything.
Unicode and Non-ASCII Characters: Optional But Useful
Characters above U+007F — accented letters, CJK, emoji, arrows — are technically allowed in JSON strings as-is, provided the file is encoded as UTF-8 (which RFC 8259 requires). A string like {"city": "São Paulo"} is valid JSON.
However, many systems are safer with pure-ASCII JSON. In those contexts every non-ASCII character should be written as a \uXXXX sequence, and characters above U+FFFF (such as emoji) require a surrogate pair:
😀 → 😀
Per the Unicode Consortium's emoji data set, as of Unicode 15.1 there are 3,782 basic emoji code points — each one above U+FFFF must be split into a high surrogate (\uD800–\uDBFF) and a low surrogate (\uDC00–\uDFFF) if you target parsers that do not handle raw UTF-8.
When you are working with JavaScript source code that embeds JSON strings inside JSON.stringify calls or string literals, the JavaScript String Escaper handles both JSON mode and template-literal mode in the same interface, which saves a context switch when you are bouncing between the two.
The Three Rules That Catch 95% of JSON Escaping Bugs
After debugging dozens of malformed payloads in production API work, I have converged on three habits that prevent the vast majority of escaping errors:
1. Never build JSON by string interpolation. The moment you write '{"key": "' + value + '"}' you are one special character away from a broken payload. Use your language's native serializer (JSON.stringify, json.dumps, encoding/json.Marshal, etc.) and pass the value as a data structure, not a pre-built string.
2. Always validate before sending. A 300-millisecond validation pass against a local parser (or a tool like the one above) catches problems before they hit a production endpoint. It is much cheaper than debugging a 400 error in a live system.
3. Treat the control character range (U+0000–U+001F) as hazardous. If your string contains any of these — raw newlines, tabs, nulls — serialize through a proper encoder. They are the most common source of "valid at a glance" JSON that a strict parser still rejects.
These three rules do not require memorizing the full escape table. They mean the serializer handles the table for you, and you only reach for the table when you are reading a broken payload someone else produced.
Quick Reference Card
Must escape (always):
" → \"
\ → \\
U+0000–U+001F → \uXXXX (or named: \b \f \n \r \t)
Optional escape:
/ → \/ (only needed inside <script> blocks)
Non-ASCII → \uXXXX (only needed for ASCII-safe output)
Never escape:
' (apostrophe — safe in JSON strings)
& (ampersand — only special in HTML/XML)
<> (angle brackets — only special in HTML/XML)
The JSON String Escape and Unescape Tool applies this exact ruleset and lets you paste a raw string on one side and get the escaped JSON string value on the other — useful for double-checking a specific input before committing it to a payload.
Made by Toolora · Updated 2026-07-01