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

  1. Convert the offending value to a string before adding it (fmt.Sprintf("%v", v) or strconv) so ctyify sees a string
  2. Only populate TaskEnv maps via the documented builder APIs, which enforce string values
  3. Check the key name and %T in the message to locate the code inserting the wrong type
  4. 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

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


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/cbb4b1d21b96d616. Report an issue: GitHub.