Unterminated String in JSON: Every Cause and Fix
Recent V8 (Chrome, Edge and Node; Node 22 is what I ran here) throws SyntaxError: Unterminated string in JSON at position N when JSON.parse() opens a string with a double quote and runs out of input before it finds the closing one. It is a different message from Unexpected end of JSON input, and it has one quirk that sends people looking in the wrong place: the reported position is always the end of your text, never the spot where the quote went missing. Every example below was run in Node 22 before it went into this page, and each links to a prefilled JSON Lab so you can watch the same error appear on the same input.
Why the position is always the end
A JSON string has no length prefix. The parser reads characters until it meets an unescaped ", and only when it hits the end of the input can it say the string was never closed. So the position is where the input ended. Look at the five single-line examples in this post: the position in each one equals the number of characters in the input.
const text = '{"id": 7, "name": "Ada}';
text.length; // 23
JSON.parse(text);
// SyntaxError: Unterminated string in JSON at position 23 (line 1 column 24)
That is why this error is the opposite of Unexpected token, where the position points at the offending character. Here you have to work backwards from the end: find the last opening quote in the text and ask why nothing closed it.
| Message | Where the input ended | Typical cause |
|---|---|---|
Unterminated string in JSON at position N | Inside a string | Truncated text, missing or curly closing quote, a backslash before the closing quote |
Unexpected end of JSON input | Anywhere else that is incomplete (after :, after ,, an empty string) | Truncated text, empty response body |
Expected ',' or '}' after property value in JSON at position N | After a value, before the next separator | Truncated text right after a number or literal, a missing closing brace |
1. The payload was cut off in the middle of a string
The most common cause by far. The JSON was fine when it left the sender and something shortened it on the way: a proxy or log line with a size limit, a database column that is too short, a buffer that was read before the stream finished, a substring() somewhere, a clipboard that lost the end. If the cut lands inside a string value, you get this error.
JSON.parse('{"id": 7, "message": "Deployment finished su');
// SyntaxError: Unterminated string in JSON at position 44 (line 1 column 45)
The text above is 44 characters long, and the error is at position 44. When the message ends mid-word like that (su), you know it is truncation and not a typo, so stop reading the JSON and go upstream: compare the byte count the sender produced with what the receiver got, and check every limit between them. Retrying the parse will never help.
See it fail live
A response cut off mid-string, error at position 44 (the end of the text):
Open this example in JSON Lab2. Parsing a partial streaming chunk
Server-sent events, chunked HTTP bodies and LLM streaming APIs deliver JSON in pieces, and the pieces do not respect string boundaries. Calling JSON.parse() on each chunk as it arrives works until a chunk ends inside a string:
JSON.parse('{"choices":[{"delta":{"content":"Hel');
// SyntaxError: Unterminated string in JSON at position 36 (line 1 column 37)
Buffer instead of parsing eagerly. For newline-delimited streams, accumulate bytes until you see a newline and parse each complete line; for an event stream, accumulate until the blank line that ends the event. If you only need the first complete value from a growing buffer, a streaming parser is the right tool, not a try/catch around JSON.parse() that swallows the error and retries on the next chunk.
See it fail live
A streaming chunk that ends inside a string value (position 36):
Open this example in JSON Lab3. The last string is missing its closing quote
A hand-edited file, a template that forgot a character, or a quote deleted by a find-and-replace. The tricky part is that a missing quote in the middle of a document usually produces a different error, because the next quote the parser meets closes the string you thought was still open and the rest of the line stops making sense. The "Unterminated" message appears when the broken string is the last one in the text, so there is no later quote to be mistaken for its end:
JSON.parse('{"id": 7, "name": "Ada}');
// SyntaxError: Unterminated string in JSON at position 23 (line 1 column 24)
Here the closing brace was swallowed into the string, so the parser hit the end of the input inside Ada}. The fix is the missing " before the brace. If you are producing this text from code, the lesson is the usual one: build a value and call JSON.stringify() on it instead of gluing quotes together by hand.
See it fail live
A missing closing quote on the final value (position 23):
Open this example in JSON Lab4. A curly quote where a straight quote should be
Text pasted from Word, Notion, Slack, a PDF or a rich-text editor often has its straight quotes replaced by typographic ones. JSON only accepts the ASCII " (U+0022). The curly ” (U+201D) is just an ordinary character inside a string, so a string that is "closed" with one is never closed at all:
JSON.parse('{"name": "Ada”}');
// SyntaxError: Unterminated string in JSON at position 15 (line 1 column 16)
The same slip in the middle of an object reports something else: {"name": "Ada”, "id": 7} fails with Expected ',' or '}' after property value in JSON at position 17, because the next straight quote (the one that opens "id") closes the runaway string and the parser then trips over id. If your error message and the visible text don't seem to agree, check for curly quotes first; they look almost identical in most fonts. The fix is to replace them with straight quotes, and to find out which editor or channel inserted them.
See it fail live
A curly closing quote, so the string never ends (position 15):
Open this example in JSON Lab5. A backslash eating the closing quote
Inside a JSON string, \" is an escaped quote, a literal quote character that does not end the string. So a value whose last character is a lone backslash, most often a Windows path, turns its own closing quote into content:
JSON.parse('{"dir": "C:\\\\logs\\"}'); // the JSON text is {"dir": "C:\\logs\"}
// SyntaxError: Unterminated string in JSON at position 20 (line 1 column 21)
In the text the parser sees, the first \\ pair is a valid escaped backslash, but the final \" escapes the quote, so the string runs on through } to the end of the input. A path that ends with a backslash needs it written as two: "C:\\logs\\". Do not escape by hand. JSON.stringify({ dir: 'C:\\logs\\' }) produces the right text every time.
See it fail live
A trailing lone backslash escapes the closing quote (position 20):
Open this example in JSON Lab6. A string fragment instead of a value
Sometimes the thing handed to JSON.parse() is just the front half of a string: it was split out of a log line with cut, split(': ') or a regex that kept the opening quote and dropped the closing one, or it was assembled as '"' + value and the closing quote never got appended.
JSON.parse('"hello');
// SyntaxError: Unterminated string in JSON at position 6 (line 1 column 7)
A bare string is a valid JSON document on its own (JSON.parse('"hello"') returns "hello"), which is why this fails with the same message as a full object. Fix the extraction so the quotes stay balanced, or skip parsing entirely if you only wanted the text.
Why you sometimes get a different error: the trailing newline
Real files end with a newline, and a raw newline is not allowed inside a JSON string. So if the text from section 3 has a trailing line break (the normal state of any file saved by an editor), the parser rejects the newline before it reaches the end of the input, and the message changes:
JSON.parse('{"id": 7, "name": "Ada}\n');
// SyntaxError: Bad control character in string literal in JSON at position 23 (line 1 column 24)
Same root cause, an unclosed string, but reported at the newline instead of at the end. That is the other reason this error misleads: the message names a control character, and the real problem is the quote you can't see. If you got "Bad control character" on text that has no obviously raw newline inside a value, look for a string that was never closed.
See it fail live
The same missing quote, plus a trailing newline, reports Bad control character:
Open this example in JSON LabDebugging checklist
- Compare the reported position with the length of your input. If they match, the parser reached the end of the text inside a string, which is exactly what "unterminated" means.
- Look at how the text ends. Mid-word or mid-token means truncation: stop debugging the JSON and check size limits, buffers and partial reads upstream.
- If the ending looks complete, find the last opening quote and check it has a straight
"partner: look for curly quotes, a missing quote before a closing brace, and a lone backslash before a closing quote. - If you got "Bad control character" instead, check whether the text ends with a newline after an unclosed string.
- Fix the producer (
JSON.stringify(),json.dumps()) or the transport, not the parse call. Do not wrap it in a retry loop.
Related errors with different causes: Unexpected end of JSON input covers the other truncation cases, Unexpected token in JSON covers the wrong-character cases (HTML error pages, BOMs, undefined), and Expected property name or '}' covers misplaced keys and commas.
📋 Paste it and see exactly where it breaks
JSON Lab formats and validates entirely in your browser, so nothing you paste is uploaded. The optional AI Repair button is the exception: it sends your input to Google's Gemini API.
Open JSON Lab →