{"record":{"id":"9f4f6877b17a5d9b","repo":"toon-format/toon","slug":"expected-expected-itemtype-but-got-actual","errorCode":null,"errorMessage":"Expected ${expected} ${itemType}, but got ${actual}","messagePattern":"Expected (.+?) (.+?), but got (.+?)","errorType":"exception","errorClass":"ToonDecodeError","httpStatus":null,"severity":"error","filePath":"packages/toon/src/decode/validation.ts","lineNumber":16,"sourceCode":"import type { ArrayHeaderInfo, BlankLineInfo, Delimiter, Depth, ParsedLine } from '../types.ts'\nimport { COLON, LIST_ITEM_PREFIX } from '../constants.ts'\nimport { findUnquotedChar } from '../shared/string-utils.ts'\nimport { ToonDecodeError } from './errors.ts'\n\n// #region Count and structure validation\n\nexport function assertExpectedCount(\n  actual: number,\n  expected: number,\n  itemType: string,\n  options: { strict: boolean },\n  line: ParsedLine,\n): void {\n  if (options.strict && actual !== expected) {\n    throw new ToonDecodeError(\n      `Expected ${expected} ${itemType}, but got ${actual}`,\n      { line: line.lineNumber, source: line.raw },\n    )\n  }\n}\n\nexport function validateNoExtraListItems(\n  nextLine: ParsedLine | undefined,\n  itemDepth: Depth,\n  expectedCount: number,\n): void {\n  if (nextLine?.depth === itemDepth && nextLine.content.startsWith(LIST_ITEM_PREFIX)) {\n    throw new ToonDecodeError(\n      `Expected ${expectedCount} list-form items, but found more`,\n      { line: nextLine.lineNumber, source: nextLine.raw },\n    )\n  }\n}","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/toon-format/toon/blob/604eac266e35166bed6f5b9e3bd586e9c60a9330/packages/toon/src/decode/validation.ts#L1-L34","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nitems[3]{id}\n  1\n  2\n  3\n  4\n// after\nitems[4]{id}\n  1\n  2\n  3\n  4","handlingStrategy":"validation","validationCode":"const declared = header.match(/\\[(\\d+)\\]/)?.[1]\nconst actual = body.filter(l => isItem(l)).length\nif (Number(declared) !== actual) throw new Error(`header says ${declared}, body has ${actual}`)","typeGuard":"function countsMatch(headerCount: number, items: unknown[]): boolean { return items.length === headerCount }","tryCatchPattern":"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) }","preventionTips":["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"],"tags":["toon","strict-mode","decode","validation"],"backgroundTag":"count-mismatch","analyzedSha":"604eac266e35166bed6f5b9e3bd586e9c60a9330","analyzedAt":"2026-08-31T11:09:25.043Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}