toon-format/toon · error · ToonDecodeError

Expected ${header.length} 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.

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)

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.