{"post":{"slug":"streaming-json-parsing-2026","title":"Streaming JSON: Parsing Something That Is Not Valid Yet","excerpt":"You want to show structured output as it arrives, but partial JSON does not parse. Handling that properly is the difference between a responsive interface and a spinner.","category":"ai","date":"January 13, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">S</span>treaming a text response is easy — append each chunk and display it. Streaming a structured response is not, because the intermediate states are not valid documents. `{\"title\": \"Quarterly rep` is what you have after a few chunks, and no parser accepts it.\n\nThe naive response is to buffer everything and parse at the end, which discards the entire benefit of streaming and leaves the user watching a spinner while data is already arriving.\n\n## What changed in 2026\n\n- **Structured outputs made this common.** Once schema-constrained generation became reliable, streaming structured responses became a normal product requirement.\n- **Repair-based parsing became the standard approach.** Completing partial documents and parsing the result displaced hand-written incremental parsers.\n- **Field-level streaming emerged in tooling.** Libraries that emit completed fields as they finalise reduced the amount of custom code needed.\n- **Agent UIs raised the stakes.** Showing tool arguments as they stream became a common interface pattern.\n\n## Repair, then parse\n\nThe technique that works and is much simpler than writing an incremental parser: take the partial text, close whatever is open, and parse the result.\n\nTrack the structure as you go — which brackets and braces are unclosed, whether you are inside a string, whether an escape is pending — then append the closing characters needed to make it syntactically complete. Parse that with an ordinary parser.\n\nYou get a valid object representing everything received so far, with the incomplete tail either truncated or present as a partial value. Recompute it on each chunk; the cost is negligible relative to the model call producing the tokens.\n\nEdge cases that need care: a partial escape sequence inside a string, a partial Unicode escape, a trailing comma where the next element has not arrived, and a number that may still gain digits. Each is handled by dropping the incomplete trailing element rather than trying to interpret it.\n\n## Which fields are safe to show\n\nThe critical distinction: a field whose value is fully received will not change; a field still arriving will.\n\n| State | Safe to render? |\n| --- | --- |\n| Earlier fields, complete | Yes |\n| Current field, string mid-arrival | Only if you accept it growing |\n| Current field, number mid-arrival | No — digits may follow |\n| Array elements before the last | Yes |\n| The last array element | No, until the next one starts |\n| Any field not yet started | No |\n\nThe rule that follows: **emit the stable prefix, buffer the volatile tail.** Everything before the currently-arriving element is final. The current element is not.\n\nFor a string being appended to, growing text in place is usually acceptable and reads naturally — that is what text streaming already looks like. For a number, it is not: rendering `4` and then `42` and then `420` shows the user three different wrong values.\n\nSo the pragmatic policy is to stream string values character by character where that suits the interface, and to hold everything else until complete.\n\n## When not to stream structured output\n\nIf the whole response is small, streaming buys nothing. The complexity of partial parsing is only worth it when the response is large enough that waiting is noticeable.\n\nIf the consumer is code rather than a person, streaming is usually pointless — the code cannot act on a partial object anyway, so waiting for completion is simpler and safer.\n\nAnd if any field requires validation before use, streaming it to a user before validation means potentially showing something you will then reject. Validate first for anything where a wrong value has consequences — see [LLM output validation](/blog/llm-output-validation-2026).\n\nThe strongest case for streaming structured output is a long response where early fields are independently useful: a report with sections, a list that grows, or an agent's tool arguments shown as they form.\n\n## Common mistakes\n\n- **Parsing each chunk with a standard parser.** Fails until the final chunk.\n- **Rendering incomplete numbers.** Shows a sequence of wrong values.\n- **Rendering the last array element.** It may still be growing.\n- **Buffering everything.** Discards the benefit of streaming.\n- **Not handling partial escapes.** A common crash in repair logic.\n- **Streaming to code.** No benefit; adds complexity.\n- **Showing unvalidated values.** May be retracted.\n\n## FAQ\n\n**Does schema-constrained generation guarantee valid partial output?**\nIt guarantees the *final* output is valid. Intermediate states are still incomplete documents, so partial parsing is still required.\n\n**Should I write my own repair logic?**\nLibraries exist and handle the edge cases. Writing it yourself is instructive and prone to the same partial-escape bugs everyone hits.\n\n**How often should I re-parse?**\nEvery chunk is fine — repair and parse on a small document is trivially cheap relative to generation. Batching a few chunks reduces UI churn if rendering is expensive.\n\n**What about streaming tool call arguments?**\nThe same technique applies, and it is a good use case: showing the tool and its arguments as they form makes an agent's behaviour visible — see [tool schema design](/blog/tool-schema-design-2026).\n\n## Where to go next\n\nFor the generation side that produces these documents, read [structured outputs](/blog/structured-outputs-llm-2026). For validating what arrives, [LLM output validation](/blog/llm-output-validation-2026), and for the latency metrics streaming improves, [TTFT vs TPOT](/blog/ttft-vs-tpot-2026).","hue":206,"metaTitle":"Streaming JSON: Parsing Output That Is Not Valid Yet","metaDescription":"How to parse partial JSON from a streaming LLM response, why completing the structure works, and which fields are safe to render before the stream ends.","keywords":["streaming json parsing","partial json llm","incremental parsing","structured output streaming","ui streaming structured","json repair","llm streaming","frontend llm 2026"],"tldr":["**Partial JSON is invalid JSON** — a standard parser fails on every chunk until the last one.","**Complete the structure to parse it** — close open strings, arrays, and objects, then parse the repaired text.","**Only render fields that are complete** — a half-arrived string value will change.","**Buffer the tail, emit the stable prefix** — the last incomplete element is the only volatile part.","**Skip:** streaming structured output when the whole response is small. Wait for it and parse once."],"readTime":"4 min read","wordCount":876},"related":[{"slug":"streaming-responses-llm-2026","title":"Streaming LLM Responses: Perceived Speed Is the Product","excerpt":"Streaming does not make generation faster, it makes waiting tolerable. Here is how to implement it well, what breaks with structured output, and how to handle failure mid-stream.","category":"ai","date":"July 8, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":209},{"slug":"llm-output-validation-2026","title":"LLM Output Validation: Trusting Nothing the Model Returns","excerpt":"Structured outputs guarantee shape, not correctness. A perfectly valid JSON object can contain a hallucinated ID, an impossible date, and a total that does not add up.","category":"ai","date":"August 12, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":216},{"slug":"constrained-decoding-explained-2026","title":"Constrained Decoding: Making Model Output Always Parse","excerpt":"Structured output modes guarantee valid JSON by restricting what tokens the model may emit. Here is how that works, what it costs, and where it quietly hurts quality.","category":"ai","date":"August 11, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":213}],"sameCategory":[{"slug":"agent-cost-attribution-2026","title":"Agent Cost Attribution: Finding Where the Tokens Went","excerpt":"A single monthly API bill tells you that agents are expensive. It does not tell you which feature, which customer, or which retry loop is responsible. That gap is fixable.","category":"ai","date":"August 19, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":220},{"slug":"human-in-the-loop-agents-2026","title":"Human-in-the-Loop Agents: Where to Put the Approval Gate","excerpt":"Approve everything and the agent is a slow autocomplete. Approve nothing and you have handed irreversible actions to a system that can be talked into things.","category":"ai","date":"August 19, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":197},{"slug":"reasoning-effort-controls-2026","title":"Reasoning Effort Controls: Tuning How Hard a Model Thinks","excerpt":"Fixed thinking budgets gave way to effort levels, and the default is rarely the right setting. Here is how the dial behaves and where each level actually belongs.","category":"ai","date":"August 19, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":210},{"slug":"retrieval-metrics-explained-2026","title":"Retrieval Metrics: Measuring the Half of RAG That Fails","excerpt":"When a RAG system answers badly, the retrieval usually failed before the model ever spoke. Measuring generation quality alone hides that entirely.","category":"ai","date":"August 19, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":215}],"newer":{"slug":"react-agents-explained-2026","title":"ReAct Agents: Interleaving Reasoning and Tool Calls","excerpt":"Think, act, observe, repeat. The pattern behind most agent frameworks is simpler than the frameworks suggest, and understanding it explains most of how agents fail.","category":"ai","date":"January 13, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":220},"older":{"slug":"time-series-databases-2026","title":"Time-Series Databases: When a Normal Table Stops Coping","excerpt":"Metrics, events, and sensor readings share a shape ordinary databases handle badly at volume. Purpose-built stores exploit that shape, and a partitioned relational table gets you most of the way.","category":"code","date":"January 13, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":318}}