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.

Solutions

  1. Emit endArray to close startArray and endObject for startObject
  2. Fix nesting-state tracking in the event producer
  3. 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

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


AI-assisted analysis of toon-format/toon@604eac266e (2026-08-31). Data as JSON: /api/errors/e93b147125e9c13e. Report an issue: GitHub.

Appendix: 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)