JSON troubleshooting

JSONDecodeError: Extra data and multiple JSON values

Fix Python JSONDecodeError: Extra data and JavaScript errors after a JSON value. Distinguish one JSON document from NDJSON and parse each format correctly.

What the error means

The parser completed one JSON value and then found another non-whitespace character. A single JSON document contains one top-level value. Two adjacent objects, an NDJSON stream or a log suffix cannot be loaded as one document with json.loads or JSON.parse. First identify the format; wrapping unrelated text in an array is not a general fix.

Exact messages by runtime

Messages depend on the input and runtime version. Node and Python rows were reproduced locally. Other rows cite official documentation or source. Rows marked as fragments or templates preserve the source wording and are not complete exception transcripts. Browsers and libraries may append location details.

RuntimeMessageInput and evidence
Pythonjson.decoder.JSONDecodeError: Extra data: line 1 column 4 (char 3)json.loads("{} {}")
Reproduced in Python 3.14.4
JavaScript / V8SyntaxError: Unexpected non-whitespace character after JSON at position 3 (line 1 column 4)JSON.parse("{} {}")
Reproduced in Node.js 24.21.0
FirefoxSyntaxError: JSON.parse: unexpected non-whitespace character after JSON dataContent after a complete JSON value
MDN diagnostic wording, location suffix omitted
Safari / JavaScriptCoreJSON Parse error: Unexpected content at end of JSON literalContent after a complete JSON value
Official WebKit parser message and LiteralParser.h prefix
Go / json.Unmarshalinvalid character '{' after top-level valuejson.Unmarshal([]byte("{} {}"), &value)
Official Go 1.26 scanner source
C# / System.Text.Json'{0}' is invalid after a single JSON value. Expected end of data.Source template for a second value or suffix
Official .NET 10 resource string, path and location suffix omitted

Causes you can reproduce

Two documents were concatenated

Appending JSON objects to a file does not produce one larger JSON object. If the consumer expects one document, serialize an array containing the records.

PYTHON
import json
json.loads('{} {}')

An NDJSON file was loaded as ordinary JSON

NDJSON contains one complete JSON value per record line. Loading all lines in one call fails after the first record. Confirm the producer contract before using a line parser.

JAVASCRIPT
const text = '{"id":1}\n{"id":2}';
JSON.parse(text);

A log line or suffix follows the JSON

Debug output mixed into stdout or a response body makes the complete document invalid. Send logs to stderr or a separate logger and keep the JSON channel clean.

PYTHON
import json
json.loads('{"ok":true}\nrequest finished')

How to fix it

  1. Inspect the first unexpected character after the completed value. Determine whether it begins another JSON value or is an unrelated suffix.
  2. For one-document APIs and files, fix the producer to return one value, often an array of records.
  3. For documented NDJSON, parse each record line separately. Do not split pretty-printed multiline JSON into lines.
  4. For other streams of JSON values, use a streaming decoder and consume every value. Do not discard the suffix after the first successful parse.

Parse known NDJSON in JavaScript

This small example treats empty lines as ignorable. Each nonempty line must contain a complete value. A large stream should be processed incrementally instead of split into an array.

JAVASCRIPT
const text = '{"id":1}\n{"id":2}\n';
const records = text.split(/\r?\n/)
  .filter(line => line.trim() !== '')
  .map(line => JSON.parse(line));
// One ordinary document instead:
const document = JSON.stringify(records);

Read an NDJSON file one record at a time in Python

Iterating the file avoids loading the entire stream into memory. Include the record number when a line fails. This accepts blank lines by policy.

PYTHON
import json
with open('records.ndjson', encoding='utf-8') as stream:
    for number, line in enumerate(stream, 1):
        if not line.strip():
            continue
        try:
            record = json.loads(line)
        except json.JSONDecodeError as error:
            raise ValueError(f'Invalid record on line {number}') from error
        print(record)

Decode a sequence of JSON values in Go

Decoder.Decode reads the next value. Keep decoding until io.EOF; a single successful Decode does not establish that the rest of the input is empty.

GO
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "strings"
)

func main() {
    dec := json.NewDecoder(strings.NewReader("{} {}"))
    for {
        var value any
        err := dec.Decode(&value)
        if err == io.EOF { break }
        if err != nil { panic(err) }
        fmt.Println(value)
    }
}

Find the exact position with Tool Kumar

  1. Copy the exact input text, including its original whitespace. For HTTP failures, copy the raw response body.
  2. Open the Tool Kumar JSON validator and paste the text. The document stays in your browser.
  3. Read the line and column in the error or fixable-issue banner. For an invalid document, select Go to error to move the caret to that location.
  4. Inspect the preceding token as well as the marked character. A parser marks where it discovered the problem; the missing quote, separator or value can start earlier.

Python character offsets are zero-based, while its displayed lines and columns are one-based. V8 string positions count UTF-16 code units; Go offsets count bytes read. Unicode text can make these offsets differ. Use the original text and its line/column context rather than copying an offset between runtimes.

Tool Kumar can classify some input as fixable loose JSON. Review a proposed change before using the repair action, then validate the result against your API contract. For empty input, inspect the producer or response because there is no character to select.

Frequently asked questions

Is NDJSON valid as one JSON document?

An NDJSON stream with multiple records is not one JSON document. Each record line contains a JSON value. Use an NDJSON reader or parse the documented record lines separately.

Can I ignore everything after the first object?

Only if the format explicitly defines that behavior. Otherwise you can lose records or hide corrupt output. Fix the producer or consume the complete stream.

Should I wrap the data in square brackets?

If you control the producer and want one document, serialize an array of the records. Simply adding brackets around adjacent objects is insufficient because array elements also need commas.

Can Go decode several top-level values?

json.Decoder can read successive values through repeated Decode calls. json.Unmarshal validates one complete JSON document and rejects an additional top-level value.

Standards and diagnostic sources

RFC 8259 and ECMA-404 define JSON syntax. Diagnostic wording belongs to the parser. The table links each cited message to its source.