{"record":{"id":"8e4d6040de2687a5","repo":"jesseduffield/lazygit","slug":"unexpected-document-node-in-the-middle-of-a-yaml-t","errorCode":null,"errorMessage":"Unexpected document node in the middle of a yaml tree","messagePattern":"Unexpected document node in the middle of a yaml tree","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/utils/yaml_utils/yaml_utils.go","lineNumber":234,"sourceCode":"\t// Empty document: nothing to do.\n\tif len(rootNode.Content) == 0 {\n\t\treturn nil\n\t}\n\n\tbody := rootNode.Content[0]\n\n\tif err := walk(body, \"\", callback); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc walk(node *yaml.Node, path string, callback func(*yaml.Node, string)) error {\n\tcallback(node, path)\n\tswitch node.Kind {\n\tcase yaml.DocumentNode:\n\t\treturn errors.New(\"Unexpected document node in the middle of a yaml tree\")\n\tcase yaml.MappingNode:\n\t\tfor i := 0; i < len(node.Content); i += 2 {\n\t\t\tname := node.Content[i].Value\n\t\t\tchildNode := node.Content[i+1]\n\t\t\tvar childPath string\n\t\t\tif path == \"\" {\n\t\t\t\tchildPath = name\n\t\t\t} else {\n\t\t\t\tchildPath = fmt.Sprintf(\"%s.%s\", path, name)\n\t\t\t}\n\t\t\terr := walk(childNode, childPath, callback)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\tcase yaml.SequenceNode:\n\t\tfor i := range len(node.Content) {\n\t\t\tchildPath := fmt.Sprintf(\"%s[%d]\", path, i)","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/jesseduffield/lazygit/blob/c477a2959b229fbf3284be0d4d2904ab61ec3c94/pkg/utils/yaml_utils/yaml_utils.go#L216-L252","documentation":"The generic YAML walker (used to enumerate every key path in a document, e.g. when migrating config schemas) starts from the document's body node. Encountering a DocumentNode below the top level means the tree contains a nested document node, which well-formed single-document YAML never has, so the walk aborts.","triggerScenarios":"Walking a parsed yaml.Node tree where a child of the root document is itself Kind==DocumentNode — typically a hand-constructed node tree rather than parsed text, or multiple concatenated documents fed into one node.","commonSituations":"Programmatic construction of YAML nodes that mistakenly appends a document node as a child; multi-document YAML ('---' separators) passed where a single document is assumed.","solutions":["If building nodes in code, append the body MappingNode as the child, never a DocumentNode.","If the input has multiple documents, split them (yaml.Decoder loop) and walk each body separately.","Inspect the parsed tree to find where Kind==yaml.DocumentNode appears below the root."],"exampleFix":"// before\nroot.Content = append(root.Content, otherDoc) // otherDoc is a DocumentNode\n// after\nroot.Content = append(root.Content, otherDoc.Content[0]) // its mapping body","handlingStrategy":"validation","validationCode":"// Before walking, unwrap the document and assert no nested documents:\nfunc bodyOf(doc *yaml.Node) (*yaml.Node, error) {\n    if doc.Kind != yaml.DocumentNode || len(doc.Content) == 0 {\n        return nil, errors.New(\"expected a document node with content\")\n    }\n    body := doc.Content[0]\n    if body.Kind == yaml.DocumentNode {\n        return nil, errors.New(\"nested document node\")\n    }\n    return body, nil\n}","typeGuard":"func isDocumentNode(n *yaml.Node) bool {\n    return n != nil && n.Kind == yaml.DocumentNode\n}","tryCatchPattern":null,"preventionTips":["When constructing node trees, append mapping/sequence bodies, never DocumentNodes.","Parse multi-document YAML with yaml.Decoder and handle each document separately.","Fuzz/walk-test node utilities with synthetic trees to catch structural assumptions."],"tags":["yaml","ast","walker"],"backgroundTag":null,"analyzedSha":"c477a2959b229fbf3284be0d4d2904ab61ec3c94","analyzedAt":"2026-08-15T08:34:32.451Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}