mikefarah/yq · error

expected mapping node for block body

Error message

expected mapping node for block body

What it means

When encoding HCL blocks, the body of a block must be a mapping of attributes/sub-blocks. This error is raised by encodeNodeAttributes (used by block and if-mapping encoding paths) when the node supplied as a block body is not a mapping, e.g. it is a scalar or sequence.

Source

Thrown at pkg/yqlib/encoder_hcl.go:589

			labels := []string{childKey}
			if extraLabels, bodyNode, ok := extractBlockLabels(childVal); ok {
				labels = append(labels, extraLabels...)
				childVal = bodyNode
			}
			block := body.AppendNewBlock(blockType, labels)
			if err := he.encodeNodeAttributes(block.Body(), childVal); err != nil {
				return true, err
			}
		}
	}

	return true, nil
}

// encodeNodeAttributes encodes the attributes of a mapping node (used for blocks)
func (he *hclEncoder) encodeNodeAttributes(body *hclwrite.Body, node *CandidateNode) error {
	if node.Kind != MappingNode {
		return fmt.Errorf("expected mapping node for block body")
	}

	for i := 0; i < len(node.Content); i += 2 {
		keyNode := node.Content[i]
		valueNode := node.Content[i+1]
		key := keyNode.Value

		// Render as block or attribute depending on value type
		if he.encodeBlockIfMapping(body, key, valueNode) {
			continue
		}

		// Render attribute for non-block value
		if err := he.encodeAttribute(body, key, valueNode); err != nil {
			return err
		}
	}
	return nil

View on GitHub (pinned to 8b5af0694b)

Solutions

  1. Make the block body a mapping: `{resource: {aws_instance: {name: {...}}}}`
  2. Fix the input data so every block level is an object of attribute name => value
  3. Skip/reshape non-map entries with expressions like `del(.bad_block)` before `-o hcl`

Example fix

// before: {resource: {aws_instance: "web"}}
// after:  {resource: {aws_instance: {web: {ami: "ami-123"}}}}
Defensive patterns

Strategy: validation

Validate before calling

yq '[.. | tag] | any("!!seq" or "!!scalar" at block positions)'  # or explicitly check each block:
yq '.resource | to_entries | all(.value | type == "!!map")' input.yaml

Type guard

func blockBodiesAreMaps(root *CandidateNode) bool {
  for i := 1; i < len(root.Content); i += 2 {
    if root.Content[i].Kind != MappingNode { return false }
  }
  return true
}

Try / catch

if err := he.encodeNodeAttributes(body, blockNode); err != nil {
  if strings.Contains(err.Error(), "expected mapping node for block body") {
    // reshape the block value into a mapping and retry
  }
}

Prevention

When it happens

Trigger: Encoding HCL where a block's value is a non-map, e.g. `{resource: {aws_instance: "oops"}}` — the innermost block body is a scalar, so encodeNodeAttributes returns 'expected mapping node for block body'.

Common situations: Terraform-style YAML where a resource block accidentally holds a list instead of an object; typos in config where block names map to scalars; generated data with inconsistent nesting depth.

Related errors


AI-assisted analysis of mikefarah/yq@8b5af0694b (2026-09-05). Data as JSON: /api/errors/c2f28b9289a4cc9a. Report an issue: GitHub.