JSON troubleshooting

Unexpected end of JSON input: empty or truncated data

Find and fix empty responses and truncated JSON behind Unexpected end of JSON input, Python Expecting value at char 0 and Go EOF errors.

What the error means

The parser reached the end before it could read a complete JSON value. An empty string has no value at all. A truncated object, array or string starts a value but never finishes it. Python also reports Expecting value at char 0 for HTML and other invalid prefixes, so that message alone does not prove the input is empty.

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
JavaScript / V8SyntaxError: Unexpected end of JSON inputJSON.parse("") or JSON.parse('{"id":')
Reproduced in Node.js 24.21.0
Python, empty inputjson.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)json.loads("")
Reproduced in Python 3.14.4
Python, unfinished valuejson.decoder.JSONDecodeError: Expecting value: line 1 column 7 (char 6)json.loads('{"id":')
Reproduced in Python 3.14.4
Go / json.Unmarshalunexpected end of JSON inputEmpty or incomplete JSON bytes
Official Go 1.26 scanner source
Go / Decoder.DecodeEOFNo next value in an empty reader
io.EOF return in the official Go source; incomplete tokens can produce unexpected EOF
FirefoxSyntaxError: JSON.parse: unexpected end of dataIncomplete JSON input
MDN diagnostic wording, location suffix omitted
Safari / JavaScriptCoreJSON Parse error: Unexpected EOFEnd token encountered where a value is required
Official WebKit parser message; LiteralParser.h supplies the JSON Parse error prefix
C# / System.Text.JsonThe input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true.Empty input, source resource string
Official .NET 10 resource string, path and location suffix omitted

Causes you can reproduce

A response has no body

A 204 No Content response, a HEAD response or an empty file cannot be parsed as JSON. Handle a documented no-body result before calling the parser.

JAVASCRIPT
JSON.parse('');
// Also fails: a whitespace-only body
JSON.parse('   ');

A write or transfer ends before the value is complete

An interrupted file write or response can leave a key without its value or a container without its closing delimiter. Compare the actual bytes with what the producer intended to send.

PYTHON
import json
json.loads('{"id":')

Code builds only part of the JSON text

String concatenation can omit a closing bracket, quote or brace. Serialize the completed data structure instead of assembling its JSON text by hand.

JAVASCRIPT
const text = '{"items":[' + '1,2';
JSON.parse(text);

How to fix it

  1. Inspect the raw text and its length. For HTTP input, inspect status and headers before parsing.
  2. Handle a documented 204 or HEAD response without parsing. Reject an empty body when the API contract requires JSON.
  3. For incomplete data, fix the producer, file write or transfer. Wait for the complete body; a network chunk is not necessarily one JSON document.
  4. Use a serializer to produce the complete value, then validate the saved or received text.

Serialize a complete value in JavaScript

Only treat missing content as an empty collection if the API contract explicitly defines that behavior. Otherwise report the missing body.

JAVASCRIPT
const text = JSON.stringify({ items: [1, 2] });
const data = JSON.parse(text);

Check a local Python input before loading it

An empty input is different from the valid JSON literal null. Report empty input instead of silently replacing it.

PYTHON
import json
text = ''  # replace with the file or response body
if not text.strip():
    raise ValueError('Expected JSON, received an empty body')
data = json.loads(text)

Inspect a fetch response before parsing

Fetch does not reject for HTTP error statuses. Read the body once as text, inspect it during local debugging, then check status and media type. The 204 branch below returns null as an application choice; adapt it to your API contract. Remove debugging output after diagnosis and redact sensitive values before sharing logs.

JAVASCRIPT
async function fetchJson(url) {
  const res = await fetch(url, {
    headers: { Accept: 'application/json' },
    signal: AbortSignal.timeout(15000),
  });
  const text = await res.text(); // read the body only once
  console.debug(res.status, res.url, res.headers.get('content-type'));
  console.debug(text.slice(0, 200)); // local debugging only
  if (!res.ok) throw new Error('HTTP ' + res.status);
  if (res.status === 204) return null; // only for a documented no-body result
  const type = (res.headers.get('content-type') || '')
    .split(';')[0].trim().toLowerCase();
  if (type !== 'application/json' && !/^application\/[^/]+\+json$/.test(type)) {
    throw new Error('Expected JSON, received ' + (type || 'no Content-Type'));
  }
  if (!text.trim()) throw new Error('Expected JSON, received an empty body');
  return JSON.parse(text);
}

Inspect a Python requests response

Print r.status_code, r.headers and a preview of r.text before decoding. The Requests documentation explains why an empty response or invalid JSON can fail decoding even when the HTTP request itself succeeds.

PYTHON
import json
import requests

def fetch_json(url):
    r = requests.get(url, headers={"Accept": "application/json"}, timeout=15)
    print(r.status_code, r.url, r.headers.get("Content-Type"))
    print(repr(r.text[:200]))  # local debugging only
    r.raise_for_status()
    if r.status_code == 204:
        return None  # only for a documented no-body result
    media_type = r.headers.get("Content-Type", "").split(";")[0].strip().lower()
    is_json = media_type == "application/json" or (
        media_type.startswith("application/") and media_type.endswith("+json")
    )
    if not is_json:
        raise ValueError(f"Expected JSON, received {media_type or 'no Content-Type'}")
    if not r.text.strip():
        raise ValueError("Expected JSON, received an empty body")
    return json.loads(r.text)

Check the Go response before unmarshalling

Read and close the body, check status and media type, then use json.Unmarshal for one complete document. A SyntaxError.Offset counts bytes read. For a stream of records, use the multiple-values workflow instead.

GO
package main

import (
    "encoding/json"
    "errors"
    "fmt"
    "io"
    "mime"
    "net/http"
    "strings"
    "time"
)

func fetchJSON(url string) (any, error) {
    client := &http.Client{Timeout: 15 * time.Second}
    resp, err := client.Get(url)
    if err != nil { return nil, err }
    defer resp.Body.Close()
    body, err := io.ReadAll(resp.Body)
    if err != nil { return nil, err }
    preview := body
    if len(preview) > 200 { preview = preview[:200] }
    fmt.Printf("status=%d type=%q body=%q\n", resp.StatusCode,
        resp.Header.Get("Content-Type"), preview) // local debugging only
    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
    }
    if resp.StatusCode == http.StatusNoContent { return nil, nil }
    mediaType, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
    if err != nil { return nil, fmt.Errorf("invalid Content-Type: %w", err) }
    if mediaType != "application/json" &&
        !(strings.HasPrefix(mediaType, "application/") && strings.HasSuffix(mediaType, "+json")) {
        return nil, fmt.Errorf("expected JSON, received %s", mediaType)
    }
    var value any
    if err := json.Unmarshal(body, &value); err != nil {
        var syntax *json.SyntaxError
        if errors.As(err, &syntax) {
            return nil, fmt.Errorf("JSON error after byte %d: %w", syntax.Offset, err)
        }
        return nil, err
    }
    return value, nil
}

func main() {
    value, err := fetchJSON("https://example.com/api/data")
    if err != nil { fmt.Println(err); return }
    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 an empty string valid JSON?

A body containing zero characters is invalid JSON. A body containing two double-quote characters is a valid JSON string with an empty value.

Should I parse a 204 response?

No. HTTP 204 means No Content. Handle it as a no-body result according to your API contract.

Does Python Expecting value at char 0 always mean empty input?

No. Python also reports it for an HTML response or another invalid first character. Inspect the raw text to tell the causes apart.

Can I fix truncation by adding a closing brace?

Only if you know the complete intended data. A missing brace may be one symptom of lost values. Recover the complete file or response before changing it.

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.