toon-format/toon · error · ToonDecodeError

Over-indented line: expected depth ${expectedDepth}, but fou

Error message

Over-indented line: expected depth ${expectedDepth}, but found ${line.depth}

What it means

At the document root, TOON expects every line at depth 0. In strict mode, a line indented deeper than the root level throws overIndentedLineError, reporting the expected depth (0) and the actual depth found. This catches structurally malformed documents early.

Source

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

    throw new ToonDecodeError(
      'Top-level document must start with a key-value or array-header line',
      { line: first.lineNumber, source: first.raw },
    )
  }

  const rootSeenKeys = options.strict ? new Set<string>() : undefined
  yield { type: 'startObject' }
  yield* decodeKeyValue(first, reader, 0, options, rootSeenKeys)

  while (true) {
    const line = yield* peekLine(reader)
    if (!line) {
      break
    }

    if (line.depth !== 0) {
      if (options.strict) {
        throw overIndentedLineError(line, 0)
      }
      assertNotScalarLine(line)
      yield* readLine(reader)
      continue
    }

    yield* readLine(reader)
    yield* decodeKeyValue(line, reader, 0, options, rootSeenKeys)
  }

  yield { type: 'endObject' }
}

// #endregion

// #region Error helpers

function assertNoDepthJump(firstNestedLine: ParsedLine, parentDepth: Depth, strict: boolean): void {

View on GitHub (pinned to 604eac266e)

Solutions

  1. Remove the leading indentation from the lines being decoded
  2. Decode only the intended sub-document, not an indented fragment
  3. Set options.strict = false to tolerate over-indented lines (they are skipped per the non-strict path)
  4. Re-encode the source data to produce properly rooted TOON output

Example fix

// before (input, strict mode)
const text = "    items: 2"
const doc = decode(text)
// after
const text = "items: 2"
const doc = decode(text)
Defensive patterns

Strategy: validation

Validate before calling

function isRootedToon(text: string): boolean {
  return text.split('\n').every(line => line.trim() === '' || line[0] !== ' ' && line[0] !== '\t' || /* allow continuation */ true)
}

Try / catch

try {
  const doc = decode(text)
} catch (e) {
  if (e instanceof Error && e.message.includes('Over-indented line')) {
    // dedent the input and retry once
    const dedented = text.split('\n').map(l => l.replace(/^\s+/, '')).join('\n')
    return decode(dedented)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling decode/decodeStreamSync on input whose first meaningful lines (or any non-empty line before any top-level key) are indented, e.g. a document that starts with a line at depth 2, with strict: true (the default).

Common situations: Wrapping TOON output inside YAML/JSON artifacts and decoding the indented block; copy-pasting snippets that carry leading indentation from editors/chat; mixing strict and non-strict workflows.

Related errors


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