mikefarah/yq · error
cannot substitute with
Error message
cannot substitute with %v, can only substitute strings. Hint: Most often you'll want to use '|=' over '=' for this operation
What it means
envsubst can only interpolate environment variables into string nodes. If the node it is applied to has any tag other than "!!str" (number, boolean, map, null, etc.), yq refuses rather than coercing types. The message also hints that assignments like `.x = envsubst(...)` often should be `.x |= envsubst(...)` so the substitution applies to the existing string value.
Solutions
- Use `|=` instead of `=`: `.x |= envsubst(.x)` so substitution runs on the matched string value
- Ensure the target node is a string: quote it in YAML (`port: "${PORT}"`) or convert with `| tostring`
- Apply envsubst only to string fields, not maps/scalars/numbers
- Use string interpolation `"\(env(MY))"` semantics or strenv where appropriate
Example fix
// before .port = envsubst(.port) # .port is !!int // error: cannot substitute with !!int ... // after .port |= envsubst(.port | tostring) # or keep the YAML value quoted so it stays a string
Defensive patterns
Strategy: type-guard
Validate before calling
yq '.x | tag' file.yaml # must print !!str before envsubst
Type guard
def is_string_node(node):
return getattr(node, 'tag', '') == '!!str' Try / catch
out=$(yq '.x |= envsubst(.x)' f.yaml 2>&1) || {
echo "envsubst target not a string: $out" >&2
exit 1
} Prevention
- Quote template values in YAML so they parse as strings
- Prefer |= over = with envsubst
- Coerce with tostring before substituting
- Only apply envsubst to string fields
When it happens
Trigger: Running `.x = envsubst(.x)` where .x is a number/bool/map (its tag isn't !!str); applying envsubst to null nodes; using `=` (which re-evaluates RHS against the root) instead of `|=` so the RHS selects a non-string node.
Common situations: Substituting into numeric ports/timeouts; forgetting |= as hinted in the message; envsubst against YAML that parsed the value as a number or boolean; applying envsubst to whole documents rather than string fields.
Related errors
- cannot get keys of , keys only works for maps and arrays
- cannot join with , can only join arrays of scalars
- cannot multiply with
- cannot pick indices from type
- cannot pivot node of type
AI-assisted analysis of mikefarah/yq@8b5af0694b (2026-09-05).
Data as JSON: /api/errors/52d1b383aa2a4201.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/yqlib/operator_env.go:82
preferences := envOpPreferences{}
if expressionNode.Operation.Preferences != nil {
preferences = expressionNode.Operation.Preferences.(envOpPreferences)
}
parser := parse.New("string", os.Environ(),
&parse.Restrictions{NoUnset: preferences.NoUnset, NoEmpty: preferences.NoEmpty})
if preferences.FailFast {
parser.Mode = parse.Quick
} else {
parser.Mode = parse.AllErrors
}
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
node := el.Value.(*CandidateNode)
if node.Tag != "!!str" {
log.Warningf("EnvSubstOperator, env name: %v %v", node.Tag, node.Value)
return Context{}, fmt.Errorf("cannot substitute with %v, can only substitute strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag)
}
value, err := parser.Parse(node.Value)
if err != nil {
return Context{}, err
}
result := node.CreateReplacement(ScalarNode, "!!str", value)
results.PushBack(result)
}
return context.ChildContext(results), nil
}
View on GitHub (pinned to 8b5af0694b)