{"record":{"id":"a329287e00c774c3","repo":"toon-format/toon","slug":"incomplete-event-stream-unclosed-objects-or-array","errorCode":null,"errorMessage":"Incomplete event stream: unclosed objects or arrays","messagePattern":"Incomplete event stream: unclosed objects or arrays","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/toon/src/decode/event-builder.ts","lineNumber":176,"sourceCode":"          if (parent.currentKey === undefined) {\n            throw new Error('Primitive event without preceding key in object')\n          }\n          setOwnProperty(parent.obj, parent.currentKey, event.value)\n          parent.currentKey = undefined\n        }\n        else if (parent.type === 'array') {\n          parent.arr.push(event.value)\n        }\n      }\n\n      break\n    }\n  }\n}\n\nfunction finalizeState(state: BuildState): JsonValue {\n  if (state.stack.length !== 0) {\n    throw new Error('Incomplete event stream: unclosed objects or arrays')\n  }\n\n  if (state.root === undefined) {\n    throw new Error('No root value built from events')\n  }\n\n  return state.root\n}\n\n// #endregion\n","sourceCodeStart":158,"sourceCodeEnd":187,"githubUrl":"https://github.com/toon-format/toon/blob/604eac266e35166bed6f5b9e3bd586e9c60a9330/packages/toon/src/decode/event-builder.ts#L158-L187","documentation":"After consuming all events, the builder's container stack still holds entries, meaning some `startObject`/`startArray` events never got matching `endObject`/`endArray` events. The library requires a complete, balanced event stream to produce a JSON value and refuses to return a partially built structure.","triggerScenarios":"Feeding buildValueFromEvents or buildValueFromEventsAsync an event iterable that ends while objects/arrays are still open — e.g. truncation during streaming, a producer that omits end events on error paths, or breaking out of the source iteration early so end events are lost.","commonSituations":"Streaming decode from a network that drops the tail of the data; custom event pipelines that swallow end events on exceptions; manually slicing an event array for debugging and feeding the slice to the builder.","solutions":["Ensure the producer emits matching endObject/endArray events for every start event","Verify the input source is fully consumed (don't truncate the TOON document or stream)","If handling partial data, wrap the builder call in try/catch and treat it as incomplete input","Regenerate the events from the full input rather than hand-splicing event sequences"],"exampleFix":"// before: events = [{type:'startObject'},{type:'key',key:'a'},{type:'primitive',value:1}] // no endObject\n// after:  events = [{type:'startObject'},{type:'key',key:'a'},{type:'primitive',value:1},{type:'endObject'}]","handlingStrategy":"validation","validationCode":"function isBalanced(events) {\n  let depth = 0\n  for (const e of events) {\n    if (e.type === 'startObject' || e.type === 'startArray') depth++\n    if (e.type === 'endObject' || e.type === 'endArray') depth--\n    if (depth < 0) return false\n  }\n  return depth === 0\n}","typeGuard":null,"tryCatchPattern":"try {\n  const value = buildValueFromEvents(events)\n} catch (err) {\n  if (err instanceof Error && err.message.includes('Incomplete event stream')) {\n    // treat as partial data; request/re-read the full source\n  } else throw err\n}","preventionTips":["Consume the entire event iterable; never break early","Ensure producers emit end events even on error paths (try/finally)","Verify network streams are fully received before decoding","Don't hand-splice event arrays for testing; regenerate from full input"],"tags":["streaming","events","toon","truncation"],"backgroundTag":"truncated-input","analyzedSha":"604eac266e35166bed6f5b9e3bd586e9c60a9330","analyzedAt":"2026-08-31T11:09:25.043Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}