mikefarah/yq · error
cannot get keys of %v, keys only works for maps and arrays
Error message
cannot get keys of %v, keys only works for maps and arrays
What it means
The `keys` (and `keys_unsorted`) operator returns the keys of a map or the indices of an array. For any other node kind (scalar or null), there is no meaningful key set, so the operator fails with this error, including the offending tag in the message. It exists to catch expressions applied to the wrong node type.
Source
Thrown at pkg/yqlib/operator_keys.go:54
}
func keysOperator(_ *dataTreeNavigator, context Context, _ *ExpressionNode) (Context, error) {
log.Debugf("keysOperator")
var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
var targetNode *CandidateNode
switch candidate.Kind {
case MappingNode:
targetNode = getMapKeys(candidate)
case SequenceNode:
targetNode = getIndices(candidate)
default:
return Context{}, fmt.Errorf("cannot get keys of %v, keys only works for maps and arrays", candidate.Tag)
}
results.PushBack(targetNode)
}
return context.ChildContext(results), nil
}
func getMapKeys(node *CandidateNode) *CandidateNode {
contents := make([]*CandidateNode, 0)
for index := 0; index < len(node.Content); index = index + 2 {
contents = append(contents, node.Content[index])
}
seq := &CandidateNode{Kind: SequenceNode, Tag: "!!seq"}
seq.AddChildren(contents)
return seq
}View on GitHub (pinned to 8b5af0694b)
Solutions
- Verify the node kind first and branch: `select(kind == "map") | keys`
- Provide defaults for missing data: `.cfg // {} | keys`
- Select the actual map: `.settings | keys` instead of running keys on the root
- Convert scalars to maps upstream if keys of a wrapper object were intended
Example fix
// before
"hello" | keys
// error: cannot get keys of !!str, keys only works for maps and arrays
// after
{a: 1, b: 2} | keys # => ["a", "b"] Defensive patterns
Strategy: type-guard
Validate before calling
yq 'select(kind == "map" or kind == "seq")' input.yaml
Type guard
def keys_of(v):
return list(v.keys()) if isinstance(v, dict) else list(range(len(v))) if isinstance(v, list) else None Try / catch
out=$(yq '.cfg // {} | keys' f.yaml 2>&1) || {
echo "keys applied to non-map/non-array: $out" >&2
} Prevention
- Default optional sections: .section // {} | keys
- Branch on kind before calling keys
- Validate document shape of external inputs
- Check that the field you select is actually a map/array
When it happens
Trigger: Running `keys` on a scalar (`"hello" | keys`), on null (empty document or missing field), or on a selected field that turned out not to be a map/array.
Common situations: Assuming a YAML field is a map when it's a scalar; empty files producing null documents; optional config sections absent in some files; applying keys at the document root of a scalar-valued JSON.
Related errors
- %v (%v) cannot be subtracted from %v
- %v cannot check contained in %v
- from entries only runs against arrays
- cannot substitute with %v, can only substitute strings. Hint
- only arrays are supported for flatten
AI-assisted analysis of mikefarah/yq@8b5af0694b (2026-09-05).
Data as JSON: /api/errors/9dbe01131791ea3b.
Report an issue: GitHub.