toon-format/toon · error · ToonDecodeError

Expected tabular rows, but found more

Error message

Expected ${header.length} tabular rows, but found more

What it means

Thrown by validateNoExtraTabularRows when a tabular array (header like `items[N]{a,b}`) contains more data rows at row depth than the declared count N: the following line is at row depth, is not a list bullet, and is classified as a data row (isDataRow). This stops the decoder from silently consuming rows that belong to another structure or were added without updating the header.

Solutions

  1. Remove the extra rows so the body matches header.length
  2. Update the [N] header count to the actual row count
  3. Check the extra line — it may be a following key-value block misindented to row depth; fix its indentation instead

Example fix

// before
users[2]{id,name}
  1,Alice
  2,Bob
  3,Carol
// after
users[3]{id,name}
  1,Alice
  2,Bob
  3,Carol
Defensive patterns

Strategy: validation

Validate before calling

const rows = block.slice(1).filter(l => !l.startsWith('- '))
if (rows.length !== headerCount) throw new Error(`tabular block has ${rows.length} rows, header declares ${headerCount}`)

Type guard

function tabularRowCountsMatch(headerCount: number, rows: string[]): boolean { return rows.length === headerCount }

Try / catch

try { decode(input) } catch (e) { if (e instanceof ToonDecodeError && e.message.includes('tabular rows, but found more')) console.error('extra row near line', e.line?.lineNumber, e.line?.source) }

Prevention

When it happens

Trigger: Decoding a tabular array (decodeTabularArray) where the number of pipe/delimiter-separated rows at row depth exceeds header.length.

Common situations: Spreadsheet exports with rows appended after the count was computed; merging two row blocks; hand-editing a row but not the `[N]` header; a line that looks like a data row (delimiter before any unquoted colon) accidentally placed inside the array block.

Related errors


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

Appendix: source

Thrown at packages/toon/src/decode/validation.ts:46

  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 },
    )
  }
}

export function validateNoExtraTabularRows(
  nextLine: ParsedLine | undefined,
  rowDepth: Depth,
  header: ArrayHeaderInfo,
): void {
  if (
    nextLine?.depth === rowDepth
    && !nextLine.content.startsWith(LIST_ITEM_PREFIX)
    && isDataRow(nextLine.content, header.delimiter)
  ) {
    throw new ToonDecodeError(
      `Expected ${header.length} tabular rows, but found more`,
      { line: nextLine.lineNumber, source: nextLine.raw },
    )
  }
}

export function validateNoBlankLinesInRange(
  startLine: number,
  endLine: number,
  blankLines: BlankLineInfo[],
  strict: boolean,
  context: string,
): void {
  if (!strict)
    return

  const firstBlank = blankLines.find(
    blank => blank.lineNumber > startLine && blank.lineNumber < endLine,

View on GitHub (pinned to 604eac266e)