JSON String Escape Characters — Which Are Required and How to Fix SyntaxErrors

Quick answer

💡JSON strings require specific backslash escape sequences for quotes (\"), backslashes (\\), newlines (\n), carriage returns (\r), tabs (\t), and all control characters U+0000 through U+001F. Unescaped occurrences of any of these characters inside a JSON string cause SyntaxError. Use JSON.stringify to produce correctly escaped strings rather than building JSON by string concatenation.

Error symptoms

  • SyntaxError: Bad escaped character in JSON at position N
  • SyntaxError: Invalid escape sequence in JSON string
  • SyntaxError: Unterminated string in JSON at position N when a raw newline breaks the string
  • JSON.parse throws for a payload containing a Windows file path with backslashes
  • A regex pattern stored in JSON fails to parse because backslashes are not doubled
  • Emoji or supplementary Unicode characters cause SyntaxError when surrogate pairs are incorrectly encoded

Common causes

  • A literal newline character (U+000A) typed by pressing Enter inside a JSON string value
  • A Windows file path copied into a JSON string value without doubling the backslashes
  • A regular expression pattern pasted into JSON where single backslashes are used for character classes
  • A SQL LIKE pattern with backslash-escaped percent signs stored in JSON without proper escaping
  • An unescaped double-quote character appearing inside a JSON string that was built by string concatenation
  • Control characters (U+0000 through U+001F) embedded in text from user input or database content

When it happens

  • When a developer hand-writes a JSON configuration file and includes file paths, regex patterns, or multi-line text
  • When a server generates JSON by string concatenation rather than using a JSON serialization library
  • When a database stores raw user input that contains newlines, tabs, or backslashes and the API reads it into a JSON string without escaping
  • When a Python script generates JSON using string formatting instead of json.dumps
  • When an automated tool exports JSON and the escaping logic does not handle all required control characters

Examples and fixes

A deployment configuration file stores a log format regex pattern. The pattern uses backslash sequences for character classes, which must be doubled in JSON.

Regex pattern with backslashes in a JSON configuration file

❌ Wrong

{
  "serviceName": "payment-processor",
  "logFormat": "access",
  "logPattern": "^\d{4}-\d{2}-\d{2} \[ERROR\]",
  "outputPath": "C:\Logs\payment\access.log",
  "maxFileSizeMb": 100
}

✅ Fixed

{
  "serviceName": "payment-processor",
  "logFormat": "access",
  "logPattern": "^\\d{4}-\\d{2}-\\d{2} \\[ERROR\\]",
  "outputPath": "C:\\Logs\\payment\\access.log",
  "maxFileSizeMb": 100
}

Every backslash in a JSON string must be escaped as a double backslash. In the broken version, sequences like \d and \[ are not valid JSON escape sequences, so the parser throws SyntaxError. In the fixed version, each \d becomes \\d — two characters in the JSON text that represent one backslash in the actual string value. When this JSON is parsed by JavaScript, the resulting string contains the single backslashes that the regex engine expects. The same rule applies to the Windows file path: each backslash separator becomes two backslashes in the JSON text.

A JSON-based query configuration stores a SQL statement that spans multiple lines. Raw newlines inside JSON strings are invalid — they must be represented as the \n escape sequence.

Storing a multi-line SQL query in a JSON config without raw newlines

❌ Wrong

{
  "queryName": "monthly-revenue",
  "database": "analytics",
  "sql": "SELECT
    DATE_TRUNC('month', created_at) AS month,
    SUM(amount_cents) / 100.0 AS revenue
  FROM orders
  WHERE status = 'completed'
  GROUP BY 1
  ORDER BY 1 DESC"
}

✅ Fixed

{
  "queryName": "monthly-revenue",
  "database": "analytics",
  "sql": "SELECT\n    DATE_TRUNC('month', created_at) AS month,\n    SUM(amount_cents) / 100.0 AS revenue\n  FROM orders\n  WHERE status = 'completed'\n  GROUP BY 1\n  ORDER BY 1 DESC"
}

The JSON specification does not allow raw newline characters (U+000A) or carriage returns (U+000D) inside string values. A string that spans multiple lines in the source file contains literal newline characters, which are control characters in the range U+0000 to U+001F that must be escaped. In the fixed version, each line break is replaced with the two-character escape sequence \n, which the parser converts back to a real newline in the resulting JavaScript string. The SQL query runs identically after parsing because the database driver ignores whitespace between tokens.

Which characters must be escaped in JSON strings

The JSON specification in RFC 8259 defines exactly which characters require escape sequences inside string values. The mandatory escapes are: a double-quote character must be written as \" (backslash followed by quote), a backslash must be written as \\ (two backslashes), and all characters in the Unicode control range U+0000 through U+001F must be escaped as \uXXXX where XXXX is the four-digit hexadecimal code point. The spec also provides shorthand escape sequences for commonly used control characters: \b for backspace (U+0008), \f for form feed (U+000C), \n for newline (U+000A), \r for carriage return (U+000D), and \t for horizontal tab (U+0009).

The solidus (forward slash, U+002F) has a defined escape sequence \/ that is optional — both / and \/ are valid JSON. Some serializers use the escaped form to allow embedding JSON inside HTML script tags safely, since the sequence </script> would otherwise terminate a script block if the JSON appeared inside one.

Characters above U+001F are generally safe to include literally in JSON strings without escaping, including most non-ASCII characters and emoji. However, some serializers like Python's json.dumps with the default ensure_ascii=True setting escape all non-ASCII characters as \uXXXX sequences to produce pure ASCII output. This is valid JSON and parses correctly, but produces larger output than including the characters literally with ensure_ascii=False.

Code points above U+FFFF — including most emoji, rare CJK unified ideographs, and mathematical symbols — require surrogate pairs in JSON because they exceed the range of a single \uXXXX escape. A character like the musical notation U+1D11E MUSICAL SYMBOL G CLEF is encoded as \uD834\uDD1E — two UTF-16 surrogate half-values that JSON.parse recombines into the full code point. JavaScript strings are UTF-16 internally, so this is handled transparently. Go and other languages that use UTF-8 internally handle the recombination in their JSON parsers.

Detecting unescaped control characters in payloads

The SyntaxError thrown by JSON.parse includes a position number that indicates approximately where in the string the parser failed. For escape errors, the position points to the backslash that starts an invalid escape sequence, or to the control character that appeared literally. Using this position to find the relevant section of the JSON text is the first step.

For short JSON strings, the position is usually enough. Count characters from position 0 (some parsers report 1-based positions) to find the problematic character. For large JSON payloads, search the text for common sources of escape errors: literal newlines that appear as actual line breaks inside a string value, single backslashes not followed by a valid escape character, and unescaped double quotes inside string values.

Control characters in the range U+0000 to U+001F are invisible in most text editors, which makes them particularly difficult to spot. A null byte (U+0000), a form feed (U+000C), or a backspace (U+0008) can be present in a string that looks perfectly normal when displayed. The JSON validator at ToolDock highlights these characters distinctly when it detects them. Alternatively, grep -P '[\x00-\x1F]' in a terminal will show lines containing control characters in the range, though it cannot distinguish between escaped and literal occurrences.

For JSON generated by application code, the most effective diagnostic is to intercept the raw string just before JSON.parse is called and log its character codes around the reported position. The JavaScript code String.prototype.charCodeAt gives the numeric code point of any character, making invisible characters immediately visible as numbers. A character code of 10 is a newline, 13 is a carriage return, 9 is a tab, and any code below 32 is a control character.

Using JSON.stringify to escape strings correctly

The definitive fix for JSON escape errors in application-generated JSON is to use JSON.stringify rather than building JSON by string concatenation. JSON.stringify handles every required escape sequence automatically and completely: it escapes backslashes, double quotes, all control characters, and correctly encodes supplementary Unicode code points as surrogate pairs. There is no escape character that JSON.stringify misses, because it was written specifically to produce spec-compliant JSON.

For server-side JavaScript, any code that builds a JSON string by concatenating parts — a practice sometimes called JSON templating — should be replaced with building a JavaScript object and calling JSON.stringify. This includes patterns like 'result: { "key": "' + userInput + '" }' which are not only fragile with respect to escaping but also vulnerable to JSON injection if userInput contains characters that terminate the string early.

For Python, json.dumps is the equivalent of JSON.stringify and handles all required escaping. By default it also escapes non-ASCII characters with \uXXXX sequences. Passing ensure_ascii=False produces more compact output that includes literal UTF-8 characters, which is valid JSON but requires that the Content-Type header correctly declares charset=utf-8.

For SQL queries and regular expressions stored in JSON configuration files, write the value as a JavaScript or Python string first and then serialize it with the appropriate library. In Node.js, JSON.stringify('SELECT * FROM orders WHERE name LIKE \'%O\'Brien%\'') produces the correctly escaped JSON string representation. Do not write the JSON by hand — the escaping rules for backslashes inside both SQL and regex patterns inside JSON strings are complex enough that manual escaping reliably introduces errors.

Unicode escapes versus UTF-8 encoding strategies

The JSON specification permits string values to contain any Unicode code point, and provides two strategies for encoding them: literal UTF-8 bytes or the \uXXXX escape notation. A character like the Euro sign (U+20AC €) can appear in a JSON string either as the literal three-byte UTF-8 sequence 0xE2 0x82 0xAC or as the escape sequence \u20AC. Both are valid, and any conforming JSON parser accepts both. The choice is a matter of serializer configuration and wire format preference.

Surrogate pairs are the edge case where the two strategies diverge. Code points above U+FFFF — which includes most emoji, many rare scripts, and supplementary CJK characters — cannot be encoded as a single \uXXXX sequence because four hex digits can only represent U+0000 through U+FFFF. In the \uXXXX notation, they require two escape sequences that together form a UTF-16 surrogate pair. The emoji (U+1F600 GRINNING FACE) is encoded as \uD83D\uDE00 — two surrogate halves. Parsers that correctly implement RFC 8259 recombine the pair into the full code point.

Literal UTF-8 encoding of supplementary characters is simpler: the emoji is encoded as the four-byte UTF-8 sequence 0xF0 0x9F 0x98 0x80 and appears as a single visible character in the JSON text. All modern parsers handle this correctly. The complication arises at transport boundaries: some older systems, mail servers, or string processing code that was written for ASCII or Latin-1 may corrupt multi-byte UTF-8 sequences. For maximum compatibility across legacy systems, using \uXXXX escapes for all non-ASCII characters produces pure ASCII JSON that survives any transport layer.

JavaScript strings are internally UTF-16, so a JavaScript emoji character actually consists of two JavaScript characters — the high and low surrogate. JSON.stringify handles this transparently, emitting the correct surrogate pair escape sequences. JSON.parse recombines them. Issues arise only when code manually slices or measures JavaScript string lengths that contain supplementary characters, because the length of a surrogate pair is 2 in JavaScript's UTF-16 model.

Regex patterns with double backslashes in JSON

Regular expression patterns are among the most common sources of JSON escape errors because regex syntax uses backslashes extensively for character class shortcuts like \d (digit), \w (word character), \s (whitespace), and \b (word boundary). Each of these backslash sequences must become a double backslash in JSON: \d in a regex becomes \\d in the JSON string, \w becomes \\w, and so on.

The confusion is compounded by the fact that JavaScript source code also uses backslashes to escape characters in string literals. In JavaScript source code, a regex pattern written as a string is const pattern = '\\d+' (two backslashes in source to represent one backslash at runtime). When this runtime string is serialized to JSON with JSON.stringify, the single runtime backslash becomes \\d in the JSON output. The chain is: one backslash in the actual regex, written as two characters in JavaScript source, serialized as two characters in JSON, parsed back to one backslash at runtime. If you are confused about how many backslashes to write, let JSON.stringify and your language's regex parser handle it — build the regex object first, extract its source, and serialize that.

SQL LIKE patterns present the same problem with an additional twist. The SQL LIKE operator uses % to match any sequence of characters and _ to match any single character. To match a literal percent sign in a LIKE pattern, SQL requires escaping it as \%. This single backslash in SQL becomes \\ in a JavaScript string literal and then \\\\% — four backslashes — in a JSON string that represents a LIKE pattern containing \%. Few developers anticipate the four-backslash requirement, and the resulting JSON is both correct and visually alarming. The safest approach is to serialize SQL patterns programmatically rather than writing them by hand in JSON files.

Template strings in configuration files add another layer of potential escaping errors. Tools that support variable interpolation in JSON-like configs — like some CI/CD systems — may process escape sequences before the JSON parser does, which means backslashes need to be tripled or quadrupled depending on the evaluation order. Read your tooling's documentation carefully about escape processing order when configuration values contain backslashes.

Validating escape sequences in stored JSON data

JSON that is stored in a database, file system, or object store accumulates over time, and the source of escaping errors can be data written months or years earlier by code that has since been fixed. Validating stored JSON before processing it is a defensive practice that catches corruption from multiple sources: broken serializers, manual edits, data migrations, and import tools.

Run a validation pass over stored JSON strings when deploying schema changes or when data is first read into a service. A JSON.parse wrapped in try-catch with error logging captures positions and messages for every malformed string. Log the primary key or identifier of the record containing the bad JSON, the error message, and the character position so that the data team can investigate and repair specific records.

For databases that store JSON in text columns rather than native JSON columns (like PostgreSQL's json or jsonb types), add a check constraint or application-level validation that calls the database's JSON parsing function before insert and update. PostgreSQL's json_valid() function returns a boolean indicating whether the text is valid JSON. Enforcing this at the database level prevents invalid JSON from being stored in the first place, which eliminates the class of errors entirely rather than managing them at read time.

For APIs that receive JSON from external systems, validate the Content-Type header and the payload structure before processing. An external partner might send a payload where one string field contains an unescaped newline because their serializer has a bug. Catching this at the intake boundary with a clear error response prompts the partner to fix their serializer, rather than letting the bad data propagate into your data store where it can cause errors in every downstream system that reads it.

Quick fix checklist

  • Double every backslash in JSON string values: file paths, regex patterns, and SQL patterns all need \\ instead of \
  • Replace any raw newline characters inside JSON strings with the \n escape sequence
  • Replace raw carriage returns with \r and raw tabs with \t
  • Escape double-quote characters inside JSON strings as \"
  • Use JSON.stringify in JavaScript or json.dumps in Python to produce correctly escaped JSON automatically
  • Scan stored JSON data for control characters (U+0000 through U+001F) using grep -P '[\x00-\x1F]'
  • Use the position number from SyntaxError to find the exact problematic character in the JSON text
  • Validate all incoming JSON at API boundaries and return a 400 with the parse error message for malformed input

Related guides

Frequently asked questions

Which characters are required to be escaped in a JSON string?

RFC 8259 requires escaping the double-quote character as \", backslash as \\, and all control characters U+0000 through U+001F as \uXXXX or their shorthand equivalents (\b, \f, \n, \r, \t). Every other character — including non-ASCII Unicode characters, emoji, and extended symbols — may appear literally in a JSON string without escaping, though serializers often escape non-ASCII for ASCII-safe output.

Why does a Windows file path break JSON?

Windows paths use backslashes as directory separators, like C:\Users\alice\documents. JSON treats a single backslash as the start of an escape sequence. The sequences \U, \a, and \d are not valid JSON escape sequences, so the parser throws SyntaxError. Fix by doubling every backslash: C:\\Users\\alice\\documents. The parsed string will contain the original single backslashes that the file system expects.

Can JSON strings contain newlines?

JSON strings cannot contain literal newline characters (U+000A) or carriage returns (U+000D). Both are control characters in the mandatory-escape range U+0000 through U+001F. A string that appears to span multiple lines in a JSON file has literal newlines embedded in it, which are invalid. Replace them with the \n or \r escape sequences to represent the same characters in a spec-compliant way.

How do I store a regex pattern in a JSON file?

Regex patterns use backslashes for character classes like \d, \w, and \s. Each of these backslashes must be doubled in the JSON string value: \d becomes \\d, \w becomes \\w, and so on. A pattern like ^\d{4}-\d{2}-\d{2}$ becomes the JSON string value ^\\d{4}-\\d{2}-\\d{2}$. The easiest way is to pass the pattern to JSON.stringify in a Node.js REPL and copy the resulting string.

What does SyntaxError: Bad escaped character mean?

It means the JSON parser encountered a backslash followed by a character that is not a valid JSON escape character. Valid escape characters after a backslash are: ", \, /, b, f, n, r, t, and u (followed by four hex digits). Any other character after a backslash — like \d, \s, \p, or a letter in a Windows path — produces this error.

Does JSON.stringify always escape strings correctly?

Yes. JSON.stringify produces fully spec-compliant escape sequences for all characters that require escaping. It escapes double-quotes, backslashes, and all control characters. It also correctly handles supplementary Unicode code points by emitting surrogate pair escape sequences. Using JSON.stringify to generate JSON output rather than building strings manually eliminates all escape-related errors.

How do I escape a double-quote inside a JSON string?

Write \" (backslash followed by double-quote) in place of the literal double-quote character. For example, the string He said hello produces the JSON text "He said \"hello\"" where the outer quotes delimit the string and the inner quotes are escaped. If you are generating JSON programmatically, JSON.stringify handles this automatically — only hand-written JSON requires manual escaping.

What are surrogate pairs in JSON and when do they appear?

Surrogate pairs are pairs of \uXXXX escape sequences used to encode Unicode code points above U+FFFF — including most emoji and rare script characters. The musical symbol G clef at U+1D11E is encoded as \uD834\uDD1E, two four-hex-digit escapes that together represent the full code point. JSON.parse reassembles the pair into the correct character. If you include emoji as literal UTF-8, no surrogate pairs are needed — it is only the \uXXXX notation that requires them.

How do I find invisible control characters in JSON text?

In a terminal, use grep -P '[\x00-\x1F]' filename.json to find lines containing control characters. In JavaScript, use the charCodeAt method at the position reported by SyntaxError to get the numeric code point of the problematic character. Code below 32 is a control character. Tools like the ToolDock JSON Validator highlight these characters visually in the error context when validating a payload.

All tools run in your browser. Your data never leaves your device. Last updated: 2026-05-05.