toon-format/toon · error · ReferenceError

Expected list item

Error message

Expected list item

What it means

decodeListItem is asked to consume one list item but the line reader returned no line (end of input), so the structure promises a `- ` item that is missing. This is thrown as a plain ReferenceError, indicating truncated input rather than a styled ToonDecodeError.

Source

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

  if (options.strict && startLine !== undefined && endLine !== undefined) {
    validateNoBlankLinesInRange(startLine, endLine, reader.scanState.blankLines, options.strict, 'list-form array')
  }

  if (options.strict) {
    const nextLine = yield* peekLine(reader)
    validateNoExtraListItems(nextLine, itemDepth, header.length)
  }
}

function* decodeListItem(
  reader: LineReader,
  baseDepth: Depth,
  options: DecoderContext,
): LineRule {
  const line = yield* readLine(reader)
  if (!line) {
    throw new ReferenceError('Expected list item')
  }

  let afterHyphen: string

  if (line.content === LIST_ITEM_MARKER) {
    yield { type: 'startObject' }
    yield { type: 'endObject' }
    return
  }
  else if (line.content.startsWith(LIST_ITEM_PREFIX)) {
    afterHyphen = line.content.slice(LIST_ITEM_PREFIX.length)
  }
  else {
    throw new ToonDecodeError(
      `Expected list item to start with "${LIST_ITEM_PREFIX}"`,
      { line: line.lineNumber, source: line.raw },
    )
  }

View on GitHub (pinned to 604eac266e)

Solutions

  1. Add the missing `- ` list item line
  2. Correct the count in the array header (e.g. `[3]:` -> `[2]:`)
  3. Regenerate the document from the source data
  4. Verify the file was not truncated in transit

Example fix

// before
tags[2]:
  - a

// after
tags[2]:
  - a
  - b
Defensive patterns

Strategy: try-catch

Validate before calling

function listItemsMatchCount(text: string): boolean {
  const m = text.match(/^ *\[(\d+)\]:/m)
  if (!m) return true
  const want = Number(m[1])
  const got = text.split('\n').filter(l => /^ *- /.test(l)).length
  return got >= want
}

Type guard

function hasEnoughListItems(text: string): boolean {
  const header = text.match(/\[(\d+)\]/)
  if (!header) return true
  const count = (text.match(/^\s*- /gm) ?? []).length
  return count >= Number(header[1])
}

Try / catch

try {
  return decode(input)
} catch (e) {
  if (e instanceof ReferenceError && e.message === 'Expected list item') {
    throw new Error('Input truncated: declared list length exceeds available items')
  }
  throw e
}

Prevention

When it happens

Trigger: decodeListArray iterating N declared items calls decodeListItem, but the stream ends before the expected `- ` line — e.g. the declared array length exceeds the number of list-item lines present.

Common situations: A truncated or manually edited file where the `[3]:` header still claims 3 items but only 2 remain; a generator bug writing fewer items than declared; line-loss from a bad merge.

Related errors


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