toon-format/toon · error · SyntaxError

result.reason

Error message

result.reason

What it means

When resolving an array header that is syntactically recognizable but invalid, strict mode surfaces the header's specific reason (result.reason) as a SyntaxError, while non-strict mode returns undefined so the line is handled as ordinary content. The message text is whatever the header parser reported.

Source

Thrown at packages/toon/src/decode/decoders.ts:667

// #endregion

// #region Shared decoder helpers

// Keeps the detection/parse split free of error decisions. The bare
// SyntaxError is deliberate: the caller's `withLine` wrapper enriches it into a
// `ToonDecodeError` with a `cause`, matching the direct-throw path.
function resolveArrayHeader(
  result: ArrayHeaderParseResult,
  strict: boolean,
): { header: ArrayHeaderInfo, inlineValues?: string } | undefined {
  if (result.kind === 'notHeader') {
    return undefined
  }

  if (result.kind === 'invalid') {
    if (strict) {
      throw new SyntaxError(result.reason)
    }
    return undefined
  }

  // A valid header may still carry a strict-only violation that non-strict resolves via LWW.
  if (strict && result.strictError !== undefined) {
    throw new SyntaxError(result.strictError)
  }

  return { header: result.header, inlineValues: result.inlineValues }
}

function* yieldObjectFromFields(
  fields: readonly FieldNode[],
  primitives: readonly JsonPrimitive[],
): Generator<JsonStreamEvent> {
  let cellIndex = 0

View on GitHub (pinned to 604eac266e)

Solutions

  1. Fix the array header to valid form `[N]{fields}:` with a non-negative integer length
  2. Validate length matches the actual item count
  3. Decode with strict:false so invalid headers fall back to normal content parsing
  4. Check the generator emitting the header for template bugs

Example fix

// before
items[abc]:
  - a

// after
items[1]:
  - a
Defensive patterns

Strategy: try-catch

Validate before calling

function arrayHeadersLookValid(text: string): string[] {
  const bad: string[] = []
  for (const l of text.split('\n')) {
    const m = l.match(/^ *\[([^\]]*)\]/)
    if (m && !/^\d+$/.test(m[1])) bad.push(l.trim())
  }
  return bad
}

Type guard

function isValidArrayHeader(line: string): boolean {
  return /^\s*\[\d+\](\{[^}]*\})?\s*:/.test(line)
}

Try / catch

try {
  return decode(input, { strict: true })
} catch (e) {
  if (e instanceof SyntaxError) {
    // e.message is the header parser's result.reason
    return decode(input, { strict: false }) // invalid header parsed as regular content
  }
  throw e
}

Prevention

When it happens

Trigger: resolveArrayHeader (via headerInfo/arrayHeader, called during array decoding) gets result.kind === 'invalid' with strict:true — e.g. `[abc]:` (non-numeric length) or a malformed field-list in `{...}`.

Common situations: Hand-edited array headers with a wrong or missing length; typos like `[2,]:` or `[]:`; generated headers corrupted by template errors; mismatched braces in the field list.

Related errors


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