toon-format/toon · error · ToonDecodeError
Unexpected indentation inside keyed tabular object
Error message
Unexpected indentation inside keyed tabular object
What it means
Inside a keyed tabular object (rows under a `[N]{...}:`-style header), entry rows must all sit at the same depth. In strict mode, a line indented deeper than the established entry depth is rejected because it would represent ambiguous nesting under a row. Non-strict mode skips such lines.
Source
Thrown at packages/toon/src/decode/decoders.ts:362
const seenEntryKeys = options.strict ? new Set<string>() : undefined
let entryCount = 0
let startLine: number | undefined
let endLine: number | undefined
let lastEntryLine: ParsedLine = headerLine
yield { type: 'startObject' }
// A keyed scope ends only by dedent or end of input, so every line at entry depth
// carrying an unquoted colon is an entry row.
while (true) {
const line = yield* peekLine(reader)
if (!line || line.depth <= baseDepth) {
break
}
if (line.depth > entryDepth) {
if (options.strict) {
throw new ToonDecodeError(
'Unexpected indentation inside keyed tabular object',
{ line: line.lineNumber, source: line.raw },
)
}
yield* readLine(reader)
continue
}
if (findUnquotedChar(line.content, COLON) === -1) {
if (options.strict) {
throw new ToonDecodeError(
'Expected entry row inside keyed tabular object',
{ line: line.lineNumber, source: line.raw },
)
}
yield* readLine(reader)
continue
}View on GitHub (pinned to 604eac266e)
Solutions
- Re-indent the offending line to match the other entry rows
- Remove unintended extra nesting under the row
- Decode with strict:false to skip the over-indented line
- Normalize indentation with a formatter before decoding
Example fix
// before
users[2]{name}:
name: Ada
name: Grace
// after
users[2]{name}:
name: Ada
name: Grace Defensive patterns
Strategy: validation
Validate before calling
function tabularRowsUniformDepth(text: string): boolean {
const lines = text.split('\n').filter(l => l.trim())
let entryDepth: number | null = null
let inTable = false
for (const l of lines) {
const d = Math.floor(((l.match(/^ */) ?? [''])[0].length) / 2)
if (/^ *\[\d+\]\{.*\}:/.test(l)) { inTable = true; entryDepth = null; continue }
if (inTable) {
if (d <= 0) { inTable = false; continue }
if (entryDepth === null) entryDepth = d
else if (d !== entryDepth) return false
}
}
return true
} Try / catch
try {
return decode(input, { strict: true })
} catch (e) {
if (e instanceof ToonDecodeError && e.message.includes('inside keyed tabular object')) {
return decode(input, { strict: false })
}
throw e
} Prevention
- Keep every tabular row at exactly the same indentation depth
- Run a formatter after manual edits to tabular blocks
- Beware editors converting spaces to tabs inside tables
When it happens
Trigger: decodeKeyedObject (invoked from decodeArrayFromHeader) peeks a line whose depth exceeds the entry depth while options.strict is true — e.g. one row accidentally indented one level further than its siblings.
Common situations: Inconsistent indent after manual editing; mixed tab/space handling making one row appear deeper; copy-paste of a nested example into the middle of a tabular block.
Related errors
- Indentation depth jump: expected depth ${parentDepth + 1}, b
- Expected entry row inside keyed tabular object
- Top-level document must start with a key-value or array-head
- Unexpected bare token line outside root primitive position
- Unexpected content after the document root
AI-assisted analysis of toon-format/toon@604eac266e (2026-08-31).
Data as JSON: /api/errors/caa6e6cc9f8ba92e.
Report an issue: GitHub.