{"record":{"id":"4c4e7f2ea5cf328c","repo":"toon-format/toon","slug":"object-startobject-event-without-preceding-key","errorCode":null,"errorMessage":"Object startObject event without preceding key","messagePattern":"Object startObject event without preceding key","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/toon/src/decode/event-builder.ts","lineNumber":61,"sourceCode":"// #endregion\n\n// #region Shared event handlers\n\nfunction applyEvent(state: BuildState, event: JsonStreamEvent): void {\n  const { stack } = state\n\n  switch (event.type) {\n    case 'startObject': {\n      const obj: JsonObject = {}\n\n      if (stack.length === 0) {\n        stack.push({ type: 'object', obj })\n      }\n      else {\n        const parent = stack[stack.length - 1]!\n        if (parent.type === 'object') {\n          if (parent.currentKey === undefined) {\n            throw new Error('Object startObject event without preceding key')\n          }\n\n          setOwnProperty(parent.obj, parent.currentKey, obj)\n          parent.currentKey = undefined\n        }\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')","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/toon-format/toon/blob/604eac266e35166bed6f5b9e3bd586e9c60a9330/packages/toon/src/decode/event-builder.ts#L43-L79","documentation":"applyEvent received a startObject event while the current parent is an object with no pending currentKey. Object values must be assigned under a key, so the builder throws to reject a malformed event stream.","triggerScenarios":"buildValueFromEvents/buildValueFromEventsAsync given a stream where startObject appears inside an object context without an intervening key event.","commonSituations":"Bugs in custom event emitters/tokenizers; hand-constructed event arrays in tests missing key events.","solutions":["Emit a key event before startObject when nesting inside an object","Fix the upstream event producer's key-then-value ordering","Validate the event stream before feeding it to buildValueFromEvents"],"exampleFix":"// before\nconst events = [{type:'startObject'},{type:'endObject'}]\n// after\nconst events = [{type:'key',key:'a'},{type:'startObject'},{type:'endObject'}]","handlingStrategy":"validation","validationCode":"function keysPrecedeObjectValues(events) {\n  let inObject = false, expectKey = false\n  for (const ev of events) {\n    if (ev.type === 'startObject') { if (inObject && !expectKey) return false; inObject = true; expectKey = false }\n    else if (ev.type === 'endObject') inObject = false\n    else if (ev.type === 'key') expectKey = true\n    else expectKey = false\n  }\n  return true\n}","typeGuard":null,"tryCatchPattern":"try {\n  const value = buildValueFromEvents(events)\n} catch (e) {\n  if (e.message.includes('without preceding key')) {\n    throw new Error('Event stream violates key-before-value ordering: ' + e.message)\n  }\n  throw e\n}","preventionTips":["Always emit a key event immediately before startObject when nesting in an object","Derive event streams from encode()/the library parser rather than hand-building them","Contract-test custom emitters against the builder"],"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"}