Ollama JSON Extra Data: Fix Python Streaming Errors (2026)

ollamapythontroubleshootinglocal-ai

TL;DR: If an Ollama request returns several JSON lines, parsing the entire body as one JSON document can raise JSONDecodeError: Extra data. Set stream: false for a simple batch script, or parse complete lines for a live interface. Check the final completion event before treating saved output as finished.

What you’ll be able to do:

  • Reproduce the parsing error without downloading a model.
  • Choose between a single response and incremental text.
  • Reject interrupted generations instead of saving them as successful answers.

Honest take: Start with non-streaming for a local automation script; add streaming when someone actually needs to watch the answer arrive.

Why does a successful request produce a JSON error?

The transport format and the generated answer are separate things. Ollama’s streaming documentation describes newline-delimited JSON, or NDJSON, for streamed responses. Each line is a JSON document. The whole collection of lines is not one JSON document.

That distinction explains a frustrating symptom: the server can be reachable, the model can produce text, and the client can still fail after receiving the response. Changing the model or reinstalling a GPU driver will not repair that parser mismatch. First inspect what your client received.

The following deliberately constructed fixture reproduces the Python error. It is not captured model output and requires no Ollama installation:

import json

wire = b'{"response":"Hi","done":false}\n{"response":"!","done":true}\n'
try:
    json.loads(wire)
except json.JSONDecodeError as exc:
    print(type(exc).__name__ + ": " + exc.msg)

Expected output:

JSONDecodeError: Extra data

Python’s JSON documentation describes decoding a JSON document, not automatically collecting a series of independent documents. Catching this exception and returning an empty string conceals the actual failure. Removing newline characters also does not help: it merely puts adjacent objects on the same line.

Which response mode should you choose?

Use a single response for work that consumes a finished answer. Use streaming for work that displays progress. Neither choice establishes whether the answer is correct; that needs separate validation for your task.

Client requirementRequest choiceParsing approachFailure to watch for
Save a short completed answer"stream": falseDecode one response documentHTTP error or missing completion
Display text while it arrives"stream": trueDecode each complete NDJSON lineError event or early disconnect
Extract structured model outputBegin with non-streamingDecode envelope, then validate answerConfusing generated JSON with transport JSON
Diagnose an integrationUse an explicit modeInspect status and content typeWrapper silently changing the request

Ollama documents streaming as enabled by default on generate and chat. Set the field explicitly in application code so the intended contract is visible during review. The examples below use the native generate endpoint; they are not parsers for every compatibility API or reverse proxy.

How do you get one JSON response in Python?

Pass stream: false in the request body. Here is a standard-library client, avoiding an extra package just to demonstrate the wire contract. Replace YOUR_INSTALLED_MODEL with the exact local model name from ollama list; the placeholder is intentionally not a recommendation to download a new model.

import json
from urllib.request import Request, urlopen

payload = {
    "model": "YOUR_INSTALLED_MODEL",
    "prompt": "Reply with a short greeting.",
    "stream": False,
}
request = Request(
    "http://localhost:11434/api/generate",
    data=json.dumps(payload).encode("utf-8"),
    headers={"Content-Type": "application/json"},
    method="POST",
)
with urlopen(request, timeout=120) as response:
    result = json.load(response)

if "error" in result:
    raise RuntimeError(result["error"])
if result.get("done") is not True:
    raise RuntimeError("Response did not confirm completion")
print(result["response"])

The expected result is the model’s greeting, whose wording varies. The timeout here is an example client policy, not a measured response time or an Ollama default. Python documents urlopen’s timeout for blocking operations; do not describe it as a guaranteed total generation deadline. A production worker may need a separate overall deadline and cancellation policy.

This example lets HTTP failures raise exceptions. That is intentional: a missing model must not turn into a blank successful record. If you add exception handling, preserve the status, error category, and request identifier in your local diagnostic log. Do not indiscriminately log private prompts or document contents.

Ollama’s API introduction distinguishes local and cloud endpoints. These examples address a local server. A local URL alone does not prove your chosen model runs locally; select a downloaded local model, and avoid changing hosts or adding a cloud fallback while diagnosing a parser problem.

How do you parse a stream without losing the last piece?

Read complete lines, collect the response fragments, and require a completion event. This small function accepts any iterable of byte lines, making it useful with both an HTTP response and a deterministic fixture:

import json

def collect_generate(lines):
    parts = []
    for raw_line in lines:
        if not raw_line.strip():
            continue
        event = json.loads(raw_line)
        if "error" in event:
            raise RuntimeError(event["error"])
        parts.append(event.get("response", ""))
        if event.get("done") is True:
            return "".join(parts), event
    raise RuntimeError("Stream ended before done=true")

fixture = [
    b'{"response":"Hi","done":false}\n',
    b'{"response":"!","done":true}\n',
]
answer, final_event = collect_generate(fixture)
print(answer)
print(final_event["done"])

Expected output from this synthetic fixture:

Hi!
True

For an HTTP call, reuse the request construction above with "stream": True, then call collect_generate(response) inside the with urlopen(...) block. The function buffers the finished answer; it demonstrates safe collection, not a live user interface. To display progress, emit each fragment after decoding its line, while continuing to track whether the request completed.

A subtle implementation choice matters: append the fragment before checking done. The fixture deliberately places text on the final event. A client that checks completion first could discard that text. Keep the final event separately if your application needs metadata, instead of trying to read statistics from each earlier fragment.

The line boundary is also essential. If you later move this code to a lower-level socket reader, a network read is not necessarily a complete application message. Keep unfinished bytes in a buffer until a newline arrives. Do not feed arbitrary network chunks directly to a JSON decoder and assume each chunk contains one event.

Why must a stream check errors after HTTP success?

An initial success status does not promise that generation will finish. Ollama’s error reference explains that an error occurring after streaming starts can arrive in an error object without changing the existing HTTP status. That is why the collector checks every event rather than trusting only the response headers.

Try these fixtures after defining the function:

cases = {
    "server_error": [b'{"error":"synthetic failure"}\n'],
    "interrupted": [b'{"response":"unfinished","done":false}\n'],
}
for name, lines in cases.items():
    try:
        collect_generate(lines)
    except RuntimeError as exc:
        print(name + ": " + str(exc))

Expected output:

server_error: synthetic failure
interrupted: Stream ended before done=true

For a chat interface, showing partial text can still be useful. Mark it as interrupted, retain the error state, and give the reader a deliberate retry action. For a batch export, write to a temporary result and promote it to the completed output only after your completion and content checks pass. These are application design recommendations, not additional behavior supplied by Ollama.

Avoid automatic retries that silently append a second generation to the first. A retry is a new attempt: assign it a new identifier, clear or separate partial output, and make the final record point to the attempt that actually completed. Otherwise a downstream consumer cannot tell which text belongs to which request.

What changes when you use the chat endpoint?

The native chat endpoint places assistant text under message.content; the generate examples use response. Switching only the URL while keeping the same extraction code can therefore produce empty output even when valid events arrive. Adapt the request body and the response reader together.

For plain chat text, the fragment extraction would be:

fragment = event.get("message", {}).get("content", "")

That expression is not a complete tool-calling or thinking-output handler. If your application expects additional message fields, preserve and process them explicitly. A text-only collector should be described as text-only rather than presented as a universal Ollama client.

The official Python library is another reasonable option: its documented streaming interface yields chunks for iteration. Use the library when it simplifies your application, but still handle exceptions and incomplete work. An SDK can abstract transport parsing; it cannot decide whether a partially generated report is acceptable for your business process.

What should you check if the error remains?

Inspect a small, non-sensitive response sample before changing more settings. If it begins with HTML, you may be reaching a proxy error page or a different service. If it contains multiple JSON objects, confirm the request body actually includes the intended streaming field. If there is only one object, locate the exact parsing call that fails; your program may be trying to decode generated prose as JSON after correctly decoding the envelope.

Treat connection failures separately. A client that cannot reach the service has not yet reached this parsing problem; start with our Open WebUI and Ollama connection diagnosis. If the server rejects concurrent work, see the Ollama queue and overload guide. Neither symptom is fixed by stripping newlines.

For reproducible bug reports, record the endpoint, explicit streaming choice, client version, server version, exception class, and whether a completion event arrived. Include a synthetic fixture like the ones above whenever possible. That lets someone repair the parser without access to your private documents or your GPU.

Honest verdict: match the parser to the response

Choose non-streaming for a simple batch client. Keep streaming when incremental output is useful, and require a completion event before marking the result successful. The parser fixtures above let you verify that behavior without buying hardware or running a model.

FAQ

Q: Will a larger GPU fix JSONDecodeError: Extra data? A: Not when the cause is parsing NDJSON as one document. Fix the response reader first. Investigate hardware only if separate server logs show a resource problem.

Q: Does asking the model to return JSON disable streaming? A: No. Generated output formatting and response streaming are separate request concerns. Start with stream: false, decode the response envelope, then validate the generated content against the format your application requires.

Q: Were these examples tested on a running model? A: The deterministic parsing fixtures were checked locally with Python 3.14. The endpoint contract was checked against official documentation on November 3, 2026. No model inference, GPU benchmark, or response-time measurement is claimed here.

Sources

Last updated November 3, 2026. Examples distinguish simulated parser tests from live model execution.

Was this article helpful?

Get the numbers before you buy

New GPU and mini-PC benchmarks, VRAM thresholds, and price checks — sent when there's something worth acting on, not on a schedule. No spam, unsubscribe anytime.