toon-format/toon · error · Error
Mismatched endObject event
Error message
Mismatched endObject event
What it means
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.
Source
Thrown at packages/toon/src/decode/event-builder.ts:84
}
else if (parent.type === 'array') {
parent.arr.push(obj)
}
stack.push({ type: 'object', obj })
}
break
}
case 'endObject': {
if (stack.length === 0) {
throw new Error('Unexpected endObject event')
}
const context = stack.pop()!
if (context.type !== 'object') {
throw new Error('Mismatched endObject event')
}
if (stack.length === 0) {
state.root = context.obj
}
break
}
case 'startArray': {
const arr: JsonValue[] = []
if (stack.length === 0) {
stack.push({ type: 'array', arr })
}
else {
const parent = stack[stack.length - 1]!
if (parent.type === 'object') {View on GitHub (pinned to 604eac266e)
Solutions
- Emit endArray to close startArray and endObject for startObject
- Fix nesting-state tracking in the event producer
- Pre-validate matched start/end kinds
Example fix
// before
[{type:'startArray'},{type:'endObject'}] // mismatched
// after
[{type:'startArray'},{type:'endArray'}] Defensive patterns
Strategy: validation
Validate before calling
function endKindsMatch(events) {
const stack = []
for (const ev of events) {
if (ev.type === 'startObject') stack.push('object')
else if (ev.type === 'startArray') stack.push('array')
else if (ev.type === 'endObject') { if (stack.pop() !== 'object') return false }
else if (ev.type === 'endArray') { if (stack.pop() !== 'array') return false }
}
return stack.length === 0
} Try / catch
try {
const value = buildValueFromEvents(events)
} catch (e) {
if (e.message === 'Mismatched endObject event') {
throw new Error('Crossed nesting: endObject closed a non-object context')
}
throw e
} Prevention
- 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
When it happens
Trigger: A stream where startArray is closed by endObject (crossed nesting) passed to buildValueFromEvents/Async.
Common situations: Custom emitters mixing up begin/end event names; streaming parsers that mis-track nesting state.
Related errors
- Object startObject event without preceding key
- Unexpected endObject event
- Array startArray event without preceding key
- Unexpected endArray event
- Mismatched endArray event
AI-assisted analysis of toon-format/toon@604eac266e (2026-08-31).
Data as JSON: /api/errors/e93b147125e9c13e.
Report an issue: GitHub.