hashicorp/nomad · error
key %q has invalid type %T
Error message
key %q has invalid type %T
What it means
ctyify converts the nested map[string]any variable tree into cty values, supporting only string, cty.Value, and map[string]any. Any other Go type (int, bool, []string, nil, etc.) triggers this error naming the key and its actual %T type. AllValues calls it on the whole tree and treats failure as a programming error, since user input should already be normalized to strings.
Source
Thrown at client/taskenv/util.go:123
dst := make(map[string]cty.Value, len(src))
for k, vI := range src {
switch v := vI.(type) {
case string:
dst[k] = cty.StringVal(v)
case cty.Value:
dst[k] = v
case map[string]any:
o, err := ctyify(v)
if err != nil {
return nil, err
}
dst[k] = cty.ObjectVal(o)
default:
return nil, fmt.Errorf("key %q has invalid type %T", k, v)
}
}
return dst, nil
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Convert the offending value to a string before adding it (fmt.Sprintf("%v", v) or strconv) so ctyify sees a string
- Only populate TaskEnv maps via the documented builder APIs, which enforce string values
- Check the key name and %T in the message to locate the code inserting the wrong type
- If it originates in Nomad core, file an upstream bug — invalid user input is expected to be cleaned earlier
Example fix
// before builder.NodeAttrs["custom.count"] = 42 // after builder.NodeAttrs["custom.count"] = "42"
Defensive patterns
Strategy: validation
Validate before calling
// Go: sanitize variable maps before building the TaskEnv
func stringOnly(m map[string]any) error {
for k, v := range m {
switch v.(type) {
case string, map[string]any, cty.Value:
default:
return fmt.Errorf("key %q must be string-valued, got %T", k, v)
}
}
return nil
} Type guard
// Go
func isCtyifiable(v any) bool {
switch v.(type) {
case string, cty.Value, map[string]any:
return true
}
return false
} Try / catch
// Go
vars, perKeyErrs, err := tenv.AllValues()
if err != nil && strings.Contains(err.Error(), "has invalid type") {
return nil, fmt.Errorf("non-string variable inserted into TaskEnv: %w", err)
} Prevention
- Store all env/node attribute values as strings at the boundary
- Use the documented env builder APIs instead of mutating TaskEnv maps directly
- Run stringOnly-style validation in tests for plugin-provided attributes
- Check the %T in the message to find the code passing native Go types
When it happens
Trigger: A value in EnvMap, NodeAttrs, or TaskSecrets is not a string when AllValues -> ctyify walks it, e.g. an int/bool inserted directly into those maps, or code placing a non-string into a nested map that feeds AllValues.
Common situations: Plugins or hooks populating NodeAttrs with native Go types instead of strings; tests seeding EnvMap with non-string values; third-party code mutating TaskEnv maps directly.
Related errors
- missing node variable
- invalid type for node variable: %T
- invalid type %T
- %v couldn't be converted to boolean value
- failed to convert parsed config to map: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/cbb4b1d21b96d616.
Report an issue: GitHub.