{"record":{"id":"89d2b2db7b2a5ef9","repo":"abhigyanpatwari/GitNexus","slug":"spring-yaml-traversal-exceeds-max-yaml-traversal","errorCode":null,"errorMessage":"Spring YAML traversal exceeds ${MAX_YAML_TRAVERSAL_NODES} nodes","messagePattern":"Spring YAML traversal exceeds (.+?) nodes","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/ingestion/pipeline-phases/spring-config.ts","lineNumber":170,"sourceCode":"}\n\ninterface YamlMappingLocation {\n  readonly valueEvent: YamlParseEvent;\n  readonly line: number;\n}\n\ninterface YamlTraversalState {\n  remainingNodes: number;\n  readonly activeObjects: Set<object>;\n}\n\nfunction consumeYamlTraversalBudget(state: YamlTraversalState, depth: number): void {\n  if (depth > MAX_YAML_TRAVERSAL_DEPTH) {\n    throw new Error(`Spring YAML traversal depth exceeds ${MAX_YAML_TRAVERSAL_DEPTH}`);\n  }\n  state.remainingNodes--;\n  if (state.remainingNodes < 0) {\n    throw new Error(`Spring YAML traversal exceeds ${MAX_YAML_TRAVERSAL_NODES} nodes`);\n  }\n}\n\nfunction isObjectValue(value: unknown): value is object {\n  return value !== null && typeof value === 'object';\n}\n\n// Aliases are resolved to their anchor event by name while the tree is built,\n// so following one here is a single pointer hop.\nfunction resolveYamlAliasEvent(event: YamlParseEvent | undefined): YamlParseEvent | undefined {\n  return event?.aliasOf ?? event;\n}\n\nfunction yamlMappingPairs(event: YamlParseEvent): Array<{\n  key: string;\n  keyEvent: YamlParseEvent;\n  valueEvent: YamlParseEvent;\n}> {","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/ingestion/pipeline-phases/spring-config.ts#L152-L188","documentation":"A node-count guard inside the Spring YAML traversal (`consumeYamlTraversalBudget`). It decrements `remainingNodes` (initialized to `MAX_YAML_TRAVERSAL_NODES` = 100,000) for every node visited and throws when the budget goes negative. Because shared anchors/aliases can cause a node to be revisited, the walk can amplify a moderately-sized file; this cap bounds total work and prevents a pathological or adversarial config from stalling ingestion.","triggerScenarios":"The Spring YAML walk visits more than 100,000 nodes cumulative — the `remainingNodes` counter drops below zero in `consumeYamlTraversalBudget`. Happens with very large config files or with alias-heavy YAML where the traversal revisits shared subtrees many times.","commonSituations":"A monorepo with a massive shared Spring config referenced by many anchors; a generated YAML with thousands of repeated keys; adversarial input intended to slow ingestion.","solutions":["Split the oversized Spring YAML into smaller per-profile/per-service files.","Reduce reliance on YAML anchors that force repeated subtree revisits during traversal.","Exclude the file from indexing via `.gitnexusignore` if it is not essential to code intelligence."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await extractSpringConfig(repoPath);\n} catch (err) {\n  if (/Spring YAML traversal exceeds .* nodes/.test(err.message)) {\n    // The file exceeded the 100k-node traversal cap — log and continue,\n    // or split the config.\n    logger.warn(`Oversized Spring config skipped: ${err.message}`);\n    return;\n  }\n  throw err;\n}","preventionTips":["Split monolithic Spring configs into per-service/per-profile files.","Minimize YAML anchor reuse that forces repeated subtree traversal.","Exclude oversized non-essential config via .gitnexusignore."],"tags":["spring","yaml","config-parsing","ingestion","node-limit"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}