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

  1. If building nodes in code, append the body MappingNode as the child, never a DocumentNode.
  2. If the input has multiple documents, split them (yaml.Decoder loop) and walk each body separately.
  3. 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 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


AI-assisted analysis of jesseduffield/lazygit@c477a2959b (2026-08-15). Data as JSON: /api/errors/8e4d6040de2687a5. Report an issue: GitHub.