semaphoreui/semaphore · error
expected map for nested struct field
Error message
expected map for nested struct field %s but got %T
What it means
Returned by the reflective config-decoding helper in util/config.go when it walks into a nested struct field (by Go field name in the message) and the corresponding value in the source map is not itself a map. The %T shows the actual JSON type found (string, float64, []interface{}, bool). It is a type guard for structurally invalid config: nested sections must decode from JSON objects; any scalar or array where a section object is expected triggers this error.
Solutions
- Fix the config file/environment mapping so the named section is a JSON/YAML object with the nested field keys inside it
- Move mistyped keys that belong one level deeper under the proper parent section
- Check the %T hint — a string or number where the object is expected usually means a missing nesting level or a stray '=' style flat key
- Compare the section against config.schema.yaml to see the expected nested structure
Defensive patterns
Strategy: type-guard
When it happens
Trigger: Thrown at util/config.go:1166 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/504883bb783ba28a.
Report an issue: GitHub.
Appendix: source
Thrown at util/config.go:1166
jsonTag := field.Tag.Get("json")
if jsonTag == "" {
jsonTag = field.Name
} else {
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 stringView on GitHub (pinned to 1774ccb71a)