{"record":{"id":"317451b3030c1e66","repo":"toon-format/toon","slug":"indentation-depth-jump-expected-depth-parentdep","errorCode":null,"errorMessage":"Indentation depth jump: expected depth ${parentDepth + 1}, but found ${firstNestedLine.depth}","messagePattern":"Indentation depth jump: expected depth (.+?), but found (.+?)","errorType":"exception","errorClass":"ToonDecodeError","httpStatus":null,"severity":"error","filePath":"packages/toon/src/decode/decoders.ts","lineNumber":117,"sourceCode":"      assertNotScalarLine(line)\n      yield* readLine(reader)\n      continue\n    }\n\n    yield* readLine(reader)\n    yield* decodeKeyValue(line, reader, 0, options, rootSeenKeys)\n  }\n\n  yield { type: 'endObject' }\n}\n\n// #endregion\n\n// #region Error helpers\n\nfunction assertNoDepthJump(firstNestedLine: ParsedLine, parentDepth: Depth, strict: boolean): void {\n  if (strict && firstNestedLine.depth > parentDepth + 1) {\n    throw new ToonDecodeError(\n      `Indentation depth jump: expected depth ${parentDepth + 1}, but found ${firstNestedLine.depth}`,\n      { line: firstNestedLine.lineNumber, source: firstNestedLine.raw },\n    )\n  }\n}\n\nfunction overIndentedLineError(line: ParsedLine, expectedDepth: Depth): ToonDecodeError {\n  return new ToonDecodeError(\n    `Over-indented line: expected depth ${expectedDepth}, but found ${line.depth}`,\n    { line: line.lineNumber, source: line.raw },\n  )\n}\n\n// Both modes reject a bare token outside root primitive position, so it must not reach\n// the non-strict paths that drop an over-indented line.\nfunction assertNotScalarLine(line: ParsedLine): void {\n  const isListItem = line.content.startsWith(LIST_ITEM_PREFIX) || line.content === LIST_ITEM_MARKER\n  if (isListItem || findUnquotedChar(line.content, COLON) !== -1) {","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/toon-format/toon/blob/604eac266e35166bed6f5b9e3bd586e9c60a9330/packages/toon/src/decode/decoders.ts#L99-L135","documentation":"In strict mode, nested content may only indent exactly one level deeper than its parent. assertNoDepthJump throws when the first nested line under a key skips levels (e.g. parent at depth 0, child at depth 2), which would be ambiguous indentation in TOON. Non-strict mode tolerates the jump.","triggerScenarios":"decodeKeyValue encountering a nested block whose first line's depth exceeds parentDepth+1 while options.strict is true — typically input indented with inconsistent step sizes or mixed tab/space expansion.","commonSituations":"Editing TOON by hand and adding an extra indent level; converting YAML with 4-space indents into TOON where only 2-space steps are expected after normalization; copy-paste that re-indents a block.","solutions":["Re-indent the nested block so each level steps by exactly one depth unit","Decode with strict:false if lenient parsing is acceptable","Normalize indentation with a formatter/linter before decoding","Replace tabs with a consistent space count"],"exampleFix":"// before (depth jump 0 -> 2)\nuser:\n    name: Ada\n\n// after\nuser:\n  name: Ada","handlingStrategy":"validation","validationCode":"function hasUniformIndentStep(text: string, maxStep = 1): boolean {\n  const depths = text.split('\\n')\n    .filter(l => l.trim())\n    .map(l => Math.floor((l.match(/^ */) ?? [''])[0].length / 2))\n  return depths.every((d, i) => i === 0 || d - depths[i - 1] <= maxStep)\n}","typeGuard":null,"tryCatchPattern":"try {\n  return decode(input, { strict: true })\n} catch (e) {\n  if (e instanceof ToonDecodeError && e.message.includes('Indentation depth jump')) {\n    return decode(normalizeIndentation(input), { strict: true })\n  }\n  throw e\n}","preventionTips":["Use a consistent 2-space (one-depth) indent step everywhere","Expand tabs to spaces before decoding","Run a TOON formatter over hand-edited files","Consider strict:false when ingesting third-party files"],"tags":["toon","decoder","strict-mode","indentation"],"backgroundTag":"indentation-depth-jump","analyzedSha":"604eac266e35166bed6f5b9e3bd586e9c60a9330","analyzedAt":"2026-08-31T11:09:25.043Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}