toon-format/toon · error · Error

Mismatched endArray event

Error message

Mismatched endArray event

What it means

applyEvent popped a context on endArray but the open context was not an array (e.g. an object). Start and end kinds must match.

Source

Thrown at packages/toon/src/decode/event-builder.ts:126

        }
        else if (parent.type === 'array') {
          parent.arr.push(arr)
        }

        stack.push({ type: 'array', arr })
      }

      break
    }

    case 'endArray': {
      if (stack.length === 0) {
        throw new Error('Unexpected endArray event')
      }

      const context = stack.pop()!
      if (context.type !== 'array') {
        throw new Error('Mismatched endArray event')
      }

      if (stack.length === 0) {
        state.root = context.arr
      }

      break
    }

    case 'key': {
      if (stack.length === 0) {
        throw new Error('Key event outside of object context')
      }

      const parent = stack[stack.length - 1]!
      if (parent.type !== 'object') {
        throw new Error('Key event outside of object context')
      }

View on GitHub (pinned to 604eac266e)

Solutions

  1. Emit endObject to close startObject, endArray to close startArray
  2. Fix nesting tracking in the event producer
  3. Validate matched start/end kinds before consuming

Example fix

// before
[{type:'startObject'},{type:'endArray'}] // mismatched
// after
[{type:'startObject'},{type:'endObject'}]
Defensive patterns

Strategy: validation

Validate before calling

function endArrayKindsMatch(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 endArray event') {
    throw new Error('Crossed nesting: endArray closed a non-array context')
  }
  throw e
}

Prevention

When it happens

Trigger: A stream where startObject is closed by endArray, passed to buildValueFromEvents/Async.

Common situations: Custom emitters swapping end event names; state-tracking bugs in streaming parsers.

Related errors


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