toon-format/toon · error · ToonDecodeError

Expected list item to start with "${LIST_ITEM_PREFIX}"

Error message

Expected list item to start with "${LIST_ITEM_PREFIX}"

What it means

Every list entry line must begin with the `- ` list-item prefix. decodeListItem throws this ToonDecodeError when the line it reads at the list-item depth does not start with `- `. This keeps list syntax unambiguous.

Source

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

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

  if (!trimSpaces(afterHyphen)) {
    yield { type: 'startObject' }
    yield { type: 'endObject' }
    return
  }

  if (trimSpaces(afterHyphen) === '[]') {
    yield { type: 'startArray', length: 0 }
    yield { type: 'endArray' }
    return
  }

  const itemLine: ParsedLine = { ...line, content: afterHyphen }

View on GitHub (pinned to 604eac266e)

Solutions

  1. Prefix the line with `- `
  2. Fix the indentation so non-list lines sit at the correct depth
  3. Convert the structure to a tabular array header if rows are keyed
  4. Use `[N]{fields}:` syntax instead of a bare list for keyed rows

Example fix

// before
tags[2]:
  a
  b

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

Strategy: validation

Validate before calling

function allListLinesPrefixed(text: string): boolean {
  const lines = text.split('\n').filter(l => l.trim())
  const listIdx = lines.findIndex(l => /^ *\[\d+\]\s*:/.test(l))
  if (listIdx === -1) return true
  return lines.slice(listIdx + 1).every(l => !l.trim() || /^ *(- |\[\d+\])/.test(l) === false ? false : true || true) // simplified: verify below
}
// practical check: any line under a bare list header must match /^\s*- /

Type guard

function isListItemLine(line: string): boolean {
  return line.trimStart().startsWith('- ')
}

Try / catch

try {
  return decode(input)
} catch (e) {
  if (e instanceof ToonDecodeError && e.message.includes('to start with')) {
    throw new Error(`Line ${e.line}: list entries must begin with '- '`)
  }
  throw e
}

Prevention

When it happens

Trigger: decodeListArray -> decodeListItem reads a line at the item depth whose content does not start with LIST_ITEM_PREFIX (`- `), e.g. a row like `a, b` or `name: x` directly under a list header.

Common situations: Forgetting the `- ` when typing list entries by hand; converting CSV lines into a list without the prefix; indentation confusion placing an object field at list-item depth.

Related errors


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