toon-format/toon · error · ToonDecodeError
Unexpected content after the document root
Error message
Unexpected content after the document root
What it means
In strict mode, decodeDocument requires the entire input to be consumed by the root value; any non-empty line remaining after the root has been decoded triggers this error. This guards against concatenated documents or trailing garbage that would otherwise be silently ignored.
Source
Thrown at packages/toon/src/decode/decoders.ts:173
{ line: line.lineNumber, source: line.raw },
)
}
function keylessFieldsHeaderError(line: ParsedLine): ToonDecodeError {
return new ToonDecodeError(
'Keyless header with a field list is only valid at the document root',
{ line: line.lineNumber, source: line.raw },
)
}
// Strict decoding never silently discards input, so a line after the root form is an error.
function* assertFullyConsumed(reader: LineReader, strict: boolean): LineRule {
if (!strict) {
return
}
const line = yield* peekLine(reader)
if (line) {
throw new ToonDecodeError(
'Unexpected content after the document root',
{ line: line.lineNumber, source: line.raw },
)
}
}
function assertNoDuplicateKey(key: string, line: ParsedLine, seenKeys: Set<string> | undefined): void {
if (!seenKeys)
return
if (seenKeys.has(key)) {
throw new ToonDecodeError(
`Duplicate sibling key "${key}"`,
{ line: line.lineNumber, source: line.raw },
)
}
seenKeys.add(key)
}
View on GitHub (pinned to 604eac266e)
Solutions
- Remove the extra content after the root
- Split the file into separate documents and decode each individually
- Decode with strict:false if trailing content should be tolerated
- Wrap multiple records under one root array header
Example fix
// before name: Ada extra: orphan // after name: Ada
Defensive patterns
Strategy: validation
Validate before calling
function isSingleDocument(text: string): boolean {
const lines = text.split('\n').map(l => l.trim()).filter(Boolean)
return lines.length > 0 && !lines.slice(1).some(l => !l.startsWith('- ') && /^[^\s[][^:]*:/.test(l) === false ? false : false) // rely on decode strict for trailing check
}
// simpler pre-check: ensure only one top-level key block by decoding strict:false first and checking reader leftovers via strict decode Try / catch
try {
return decode(input, { strict: true })
} catch (e) {
if (e instanceof ToonDecodeError && e.message.includes('after the document root')) {
return input.split(/\n(?=\S)/).map(part => decode(part)) // split concatenated docs
}
throw e
} Prevention
- Never concatenate TOON documents; use one root or separate files
- Strip trailing blank/garbage lines before decoding
- Use strict:false only when trailing content is expected
When it happens
Trigger: Calling decode with strict:true on input that contains a second top-level block after the root key-value/array, or any trailing non-blank lines.
Common situations: Concatenating two TOON documents into one file; log-style appending of extra records; stray trailing line from a bad copy-paste or a diff artifact.
Related errors
- Top-level document must start with a key-value or array-head
- Indentation depth jump: expected depth ${parentDepth + 1}, b
- Unexpected bare token line outside root primitive position
- Duplicate sibling key "${key}"
- Unexpected indentation inside keyed tabular object
AI-assisted analysis of toon-format/toon@604eac266e (2026-08-31).
Data as JSON: /api/errors/86c108d8559648a5.
Report an issue: GitHub.