hashicorp/nomad · error

%q: map key is not string: %s

Error message

%q: map key is not string: %s

What it means

helper/flatmap recursively flattens arbitrary Go values into a flat string-keyed map. When traversing a reflect.Map, all keys must be strings (after unwrapping interfaces); a non-string key type cannot be represented in the flat map, so flatten panics.

Source

Thrown at helper/flatmap/flatmap.go:65

		output[prefix] = "nil"
	case reflect.Pointer:
		if primitiveOnly && enteredStruct {
			return
		}

		e := v.Elem()
		if !e.IsValid() {
			output[prefix] = "nil"
		}
		flatten(prefix, e, primitiveOnly, enteredStruct, output)
	case reflect.Map:
		for _, k := range v.MapKeys() {
			if k.Kind() == reflect.Interface {
				k = k.Elem()
			}

			if k.Kind() != reflect.String {
				panic(fmt.Sprintf("%q: map key is not string: %s", prefix, k))
			}

			flatten(getSubKeyPrefix(prefix, k.String()), v.MapIndex(k), primitiveOnly, enteredStruct, output)
		}
	case reflect.Struct:
		if primitiveOnly && enteredStruct {
			return
		}
		enteredStruct = true

		t := v.Type()
		for i := 0; i < v.NumField(); i++ {
			name := t.Field(i).Name
			val := v.Field(i)
			if val.Kind() == reflect.Interface && !val.IsNil() {
				val = val.Elem()
			}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Convert the map to map[string]interface{} (stringify keys) before passing the value to Flatten/Diff
  2. Change the containing struct so map keys are strings
  3. Stringify keys at the boundary with a helper like fmt.Sprint(k) into a new map

Example fix

// before
m := map[int]string{1: "a"}
out := flatmap.Flatten(m) // panics
// after
m2 := map[string]interface{}{"1": "a"}
out := flatmap.Flatten(m2)
Defensive patterns

Strategy: validation

Validate before calling

func hasStringKeys(v reflect.Value) bool {
    if v.Kind() != reflect.Map { return true }
    for _, k := range v.MapKeys() {
        if k.Kind() == reflect.Interface { k = k.Elem() }
        if k.Kind() != reflect.String { return false }
    }
    return true
}

Type guard

func flattenSafe(v interface{}) (m map[string]string, ok bool) {
    defer func() {
        if r := recover(); r != nil { ok = false; m = nil }
    }()
    m = flatmap.Flatten(v)
    return m, true
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        err = fmt.Errorf("flatmap: %v", r)
    }
}()

Prevention

When it happens

Trigger: Calling flatmap.Flatten or flatmap.Diff on a struct/variable payload that contains a map with non-string keys (e.g. map[int]string, map[uuid.UUID]...).

Common situations: Task/group payloads or job specs containing maps keyed by integers or custom types passed into the flatmap-based diffing code; user structs handed to Nomad's templating/diff utilities with typed map keys.

Related errors


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