jesseduffield/lazygit · error
Unexpected document node in the middle of a yaml tree
Error message
Unexpected document node in the middle of a yaml tree
What it means
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.
Source
Thrown at pkg/utils/yaml_utils/yaml_utils.go:234
// Empty document: nothing to do.
if len(rootNode.Content) == 0 {
return nil
}
body := rootNode.Content[0]
if err := walk(body, "", callback); err != nil {
return err
}
return nil
}
func walk(node *yaml.Node, path string, callback func(*yaml.Node, string)) error {
callback(node, path)
switch node.Kind {
case yaml.DocumentNode:
return errors.New("Unexpected document node in the middle of a yaml tree")
case yaml.MappingNode:
for i := 0; i < len(node.Content); i += 2 {
name := node.Content[i].Value
childNode := node.Content[i+1]
var childPath string
if path == "" {
childPath = name
} else {
childPath = fmt.Sprintf("%s.%s", path, name)
}
err := walk(childNode, childPath, callback)
if err != nil {
return err
}
}
case yaml.SequenceNode:
for i := range len(node.Content) {
childPath := fmt.Sprintf("%s[%d]", path, i)View on GitHub (pinned to c477a2959b)
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.
Example fix
// before root.Content = append(root.Content, otherDoc) // otherDoc is a DocumentNode // after root.Content = append(root.Content, otherDoc.Content[0]) // its mapping body
Defensive patterns
Strategy: validation
Validate before calling
// Before walking, unwrap the document and assert no nested documents:
func bodyOf(doc *yaml.Node) (*yaml.Node, error) {
if doc.Kind != yaml.DocumentNode || len(doc.Content) == 0 {
return nil, errors.New("expected a document node with content")
}
body := doc.Content[0]
if body.Kind == yaml.DocumentNode {
return nil, errors.New("nested document node")
}
return body, nil
} Type guard
func isDocumentNode(n *yaml.Node) bool {
return n != nil && n.Kind == yaml.DocumentNode
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- You should have an allBranchesLogCmds defined as a sequence!
- gui.sidePanels: a side panel must have at least one tab.
- gui.sidePanels must not be empty.
- gui.spinner.frames must not be empty.
- git.diffRenderers: 'command' must be specified for diff rend
AI-assisted analysis of jesseduffield/lazygit@c477a2959b (2026-08-15).
Data as JSON: /api/errors/8e4d6040de2687a5.
Report an issue: GitHub.