mikefarah/yq · error
cannot substitute with %v, can only substitute strings. Hint
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.
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)
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
- %v (%v) cannot be subtracted from %v
- %v cannot check contained in %v
- from entries only runs against arrays
- env operations have been disabled
- value for env variable '%v' not provided in env()
AI-assisted analysis of mikefarah/yq@8b5af0694b (2026-09-05).
Data as JSON: /api/errors/52d1b383aa2a4201.
Report an issue: GitHub.