{"record":{"id":"e93b147125e9c13e","repo":"toon-format/toon","slug":"mismatched-endobject-event","errorCode":null,"errorMessage":"Mismatched endObject event","messagePattern":"Mismatched endObject event","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/toon/src/decode/event-builder.ts","lineNumber":84,"sourceCode":"        }\n        else if (parent.type === 'array') {\n          parent.arr.push(obj)\n        }\n\n        stack.push({ type: 'object', obj })\n      }\n\n      break\n    }\n\n    case 'endObject': {\n      if (stack.length === 0) {\n        throw new Error('Unexpected endObject event')\n      }\n\n      const context = stack.pop()!\n      if (context.type !== 'object') {\n        throw new Error('Mismatched endObject event')\n      }\n\n      if (stack.length === 0) {\n        state.root = context.obj\n      }\n\n      break\n    }\n\n    case 'startArray': {\n      const arr: JsonValue[] = []\n\n      if (stack.length === 0) {\n        stack.push({ type: 'array', arr })\n      }\n      else {\n        const parent = stack[stack.length - 1]!\n        if (parent.type === 'object') {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/toon-format/toon/blob/604eac266e35166bed6f5b9e3bd586e9c60a9330/packages/toon/src/decode/event-builder.ts#L66-L102","documentation":"applyEvent popped a context on endObject but the context was not an object (e.g. an array was open). Start and end event kinds must match.","triggerScenarios":"A stream where startArray is closed by endObject (crossed nesting) passed to buildValueFromEvents/Async.","commonSituations":"Custom emitters mixing up begin/end event names; streaming parsers that mis-track nesting state.","solutions":["Emit endArray to close startArray and endObject for startObject","Fix nesting-state tracking in the event producer","Pre-validate matched start/end kinds"],"exampleFix":"// before\n[{type:'startArray'},{type:'endObject'}] // mismatched\n// after\n[{type:'startArray'},{type:'endArray'}]","handlingStrategy":"validation","validationCode":"function endKindsMatch(events) {\n  const stack = []\n  for (const ev of events) {\n    if (ev.type === 'startObject') stack.push('object')\n    else if (ev.type === 'startArray') stack.push('array')\n    else if (ev.type === 'endObject') { if (stack.pop() !== 'object') return false }\n    else if (ev.type === 'endArray') { if (stack.pop() !== 'array') return false }\n  }\n  return stack.length === 0\n}","typeGuard":null,"tryCatchPattern":"try {\n  const value = buildValueFromEvents(events)\n} catch (e) {\n  if (e.message === 'Mismatched endObject event') {\n    throw new Error('Crossed nesting: endObject closed a non-object context')\n  }\n  throw e\n}","preventionTips":["Emit matching end events for the kind actually opened (LIFO discipline)","Model the emitter as an explicit stack, not flags","Add a start/end kind-matching validator to CI for generated streams"],"tags":["event-stream","decoder","toon"],"backgroundTag":"malformed-event-stream","analyzedSha":"604eac266e35166bed6f5b9e3bd586e9c60a9330","analyzedAt":"2026-08-31T11:09:25.043Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}