semaphoreui/semaphore · error
cannot assign value of type %T to field
Error message
cannot assign value of type %T to field %s of type %s
What it means
assignMapToStructRecursive maps a map[string]any onto a struct via reflection. When a struct field's JSON-matching value passes the 'is a map' check but fails the typed assertion to map[string]any (it is a map with different key/value types, e.g. map[string]string), this error is returned. It signals the value's concrete Go type cannot feed the recursive struct assignment for that field.
Solutions
- Convert the value to map[string]any before calling AssignMapToStruct (copy keys/values into a new map[string]any).
- Unmarshal the source JSON into map[string]any (json.Unmarshal into any) instead of a typed map so nested values have the right type.
- Normalize decoder output (e.g. convert map[interface{}]interface{} from YAML to map[string]any) before assignment.
- If the field should not be a struct, fix the struct definition or the config key so types agree.
Example fix
// before
m := map[string]any{"db": map[string]string{"host": "localhost"}}
util.AssignMapToStruct(m, &cfg)
// after
util.AssignMapToStruct(map[string]any{
"db": map[string]any{"host": "localhost"},
}, &cfg) Defensive patterns
Strategy: type-guard
Validate before calling
func isStringAnyMap(v any) bool { _, ok := v.(map[string]any); return ok }
if !isStringAnyMap(m["db"]) { return fmt.Errorf("field db must be map[string]any") } Type guard
func asStringAnyMap(v any) (map[string]any, bool) {
if m, ok := v.(map[string]any); ok {
return m, true
}
return nil, false
} Try / catch
err := util.AssignMapToStruct(m, &cfg)
if err != nil {
var typeErr *fmt.Errorf // Inspect err.Error() for "cannot assign value of type"
_ = typeErr
log.Fatalf("config map shape invalid: %v", err)
} Prevention
- Always build config maps as map[string]any throughout, never typed maps.
- Unmarshal source documents with encoding/json into any so nested objects are map[string]any.
- Add a normalize step converting any map kinds to map[string]any before assignment.
When it happens
Trigger: Calling util.AssignMapToStruct with a value for a struct-typed field that is a reflect.Map but not a map[string]any — e.g. passing map[string]string{...} or a map[string]int under a key that maps to a nested struct field.
Common situations: Building config maps by hand with typed maps instead of map[string]any; loading config from a decoder (e.g. yaml/toml) that produces map[string]interface{} alternatives like map[interface{}]interface{}; JSON that was unmarshaled into a typed map rather than any.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- cannot assign element of type %T to slice element of type
- expected slice or json array string for field
- expected slice for field
- cannot assign element of type
- expected map for field
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/ab5f0cdacdc73463.
Report an issue: GitHub.
Appendix: source
Thrown at util/config.go:1171
jsonTag = strings.Split(jsonTag, ",")[0]
}
if value, ok := m[jsonTag]; ok {
fieldValue := structValue.FieldByName(field.Name)
if fieldValue.CanSet() {
val := reflect.ValueOf(value)
switch fieldValue.Kind() {
case reflect.Struct:
if val.Kind() != reflect.Map {
return fmt.Errorf("expected map for nested struct field %s but got %T", field.Name, value)
}
mapValue, ok := value.(map[string]any)
if !ok {
return fmt.Errorf("cannot assign value of type %T to field %s of type %s", value, field.Name, field.Type)
}
err := assignMapToStructRecursive(mapValue, fieldValue)
if err != nil {
return err
}
case reflect.Slice:
// Handle slice assignment
fieldElemType := fieldValue.Type().Elem()
var sourceSlice reflect.Value
if val.Kind() == reflect.Slice || val.Kind() == reflect.Array {
sourceSlice = val
} else if val.Kind() == reflect.String {
// Try to parse JSON array from string
str := val.String()
// First, try to unmarshal into []any
var anyArr []any
if err := json.Unmarshal([]byte(str), &anyArr); err == nil {
sourceSlice = reflect.ValueOf(anyArr)View on GitHub (pinned to 1774ccb71a)