toon-format/toon · error · ToonDecodeError
Expected ${expected} ${itemType}, but got ${actual}
Error message
Expected ${expected} ${itemType}, but got ${actual} What it means
This ToonDecodeError is thrown by assertExpectedCount when strict-mode decoding encounters an array whose declared length in the header does not match the actual number of items decoded. TOON headers declare item counts (e.g. `items[3]{id}`), and the decoder trusts and verifies them; in strict mode any mismatch aborts the decode. It only fires when options.strict is true.
Source
Thrown at packages/toon/src/decode/validation.ts:16
import type { ArrayHeaderInfo, BlankLineInfo, Delimiter, Depth, ParsedLine } from '../types.ts'
import { COLON, LIST_ITEM_PREFIX } from '../constants.ts'
import { findUnquotedChar } from '../shared/string-utils.ts'
import { ToonDecodeError } from './errors.ts'
// #region Count and structure validation
export function assertExpectedCount(
actual: number,
expected: number,
itemType: string,
options: { strict: boolean },
line: ParsedLine,
): void {
if (options.strict && actual !== expected) {
throw new ToonDecodeError(
`Expected ${expected} ${itemType}, but got ${actual}`,
{ line: line.lineNumber, source: line.raw },
)
}
}
export function validateNoExtraListItems(
nextLine: ParsedLine | undefined,
itemDepth: Depth,
expectedCount: number,
): void {
if (nextLine?.depth === itemDepth && nextLine.content.startsWith(LIST_ITEM_PREFIX)) {
throw new ToonDecodeError(
`Expected ${expectedCount} list-form items, but found more`,
{ line: nextLine.lineNumber, source: nextLine.raw },
)
}
}View on GitHub (pinned to 604eac266e)
Solutions
- Count the actual items in the array body and correct the `[N]` in the header line to match
- Add or remove items so the body has exactly N items as declared
- Decode with { strict: false } to tolerate count mismatches (if count fidelity is acceptable)
- If data is programmatically generated, compute the header count from the same array you serialize
Example fix
// before
items[3]{id}
1
2
3
4
// after
items[4]{id}
1
2
3
4 Defensive patterns
Strategy: validation
Validate before calling
const declared = header.match(/\[(\d+)\]/)?.[1]
const actual = body.filter(l => isItem(l)).length
if (Number(declared) !== actual) throw new Error(`header says ${declared}, body has ${actual}`) Type guard
function countsMatch(headerCount: number, items: unknown[]): boolean { return items.length === headerCount } Try / catch
try { decode(input, { strict: true }) } catch (e) { if (e instanceof ToonDecodeError && /but got \d+$/.test(e.message)) console.error('count mismatch at line', e.line?.lineNumber, e.line?.source) } Prevention
- Always generate the [N] header count from the serialized array's length in code
- Avoid hand-editing TOON bodies without updating the count
- Validate TOON with a linter/pre-flight decode before consuming
- Use strict:false only deliberately, never as a default workaround
When it happens
Trigger: Calling decode with strict:true (the default) on any TOON array — primitive array (decodeInlinePrimitiveArray), keyed object, tabular array (decodeTabularArray), or list array (decodeListArray) — where the header count `[N]` differs from the number of items present in the body.
Common situations: Hand-edited TOON files where rows were added/removed without updating the `[N]` header; truncated or concatenated payloads; generator code that updates data but forgets to update the count; LLM output that miscounts items.
Related errors
- Expected ${expectedCount} list-form items, but found more
- Expected ${header.length} tabular rows, but found more
- Blank lines inside ${context} are not allowed in strict mode
- Top-level document must start with a key-value or array-head
- Indentation depth jump: expected depth ${parentDepth + 1}, b
AI-assisted analysis of toon-format/toon@604eac266e (2026-08-31).
Data as JSON: /api/errors/9f4f6877b17a5d9b.
Report an issue: GitHub.