semaphoreui/semaphore · error
expected slice for field
Error message
expected slice for field %s but got %T
What it means
When a struct field is a slice, the source value must be a Go slice/array or a string parseable as a slice (JSON array, or single string for []string fields). Any other kind (int, bool, float, nil-typed value, etc.) returns this error. It reports that a non-slice value was supplied for a slice field.
Solutions
- Wrap the scalar in a slice in the source map: []any{value} or []string{value}.
- Check whether the struct field type is correct — if the value is genuinely scalar, change the field or the key.
- Validate the map values against the struct shape (reflect over structType fields) before assignment.
- If the value comes from env/file config, pre-parse it into the proper slice type.
Example fix
// before
m := map[string]any{"hosts": "web-1"} // field is []string? no — scalar in map for slice field of non-string elem
// after
m := map[string]any{"hosts": []any{"web-1"}} Defensive patterns
Strategy: validation
Validate before calling
func expectSlice(m map[string]any, key string) error {
v, ok := m[key]
if !ok { return nil }
switch v.(type) {
case []any, []string, []int, string:
return nil
default:
return fmt.Errorf("%s must be a slice or JSON array string, got %T", key, v)
}
} Type guard
func isSliceLike(v any) bool {
switch v.(type) {
case []any, []string, []int, []map[string]any:
return true
}
rv := reflect.ValueOf(v)
return rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array
} Try / catch
if err := util.AssignMapToStruct(m, &cfg); err != nil {
if strings.Contains(err.Error(), "expected slice for field") {
log.Fatalf("config value must be a list: %v", err)
}
} Prevention
- Keep config map values shape-consistent with the struct across versions.
- When promoting a scalar field to a slice, wrap legacy values in []any at the config loader.
- Validate map keys/types against the struct before calling AssignMapToStruct.
When it happens
Trigger: Calling AssignMapToStruct with m["field"] = 42 or true while the target struct field is a slice type (e.g. []string, []int).
Common situations: Config schema drift: a field changed from scalar to slice (or vice versa) and old config maps still hold a scalar; hand-built maps where the wrong key was set; merged config where a value was overwritten by a scalar default.
Related errors
- expected slice or json array string for field
- cannot assign element of type %T to slice element of type
- cannot assign element of type
- cannot assign value of type %T to field
- expected map for field
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/fa9b82ecc010c77e.
Report an issue: GitHub.
Appendix: source
Thrown at util/config.go:1198
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)
} else if fieldElemType.Kind() == reflect.String {
// Fallback: treat as single element string
sourceSlice = reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), 1, 1)
sourceSlice.Index(0).SetString(str)
} else {
return fmt.Errorf("expected slice or json array string for field %s but got %T", field.Name, value)
}
} else {
return fmt.Errorf("expected slice for field %s but got %T", field.Name, value)
}
// Build destination slice
newSlice := reflect.MakeSlice(fieldValue.Type(), 0, sourceSlice.Len())
for i := 0; i < sourceSlice.Len(); i++ {
srcElemVal := sourceSlice.Index(i)
// When source is []any, elements come as interface{}, unwrap reflect.Value
if srcElemVal.Kind() == reflect.Interface && !srcElemVal.IsNil() {
srcElemVal = reflect.ValueOf(srcElemVal.Interface())
}
var dstElem reflect.Value
// Prepare destination element
if fieldElemType.Kind() == reflect.Struct {
dstElem = reflect.New(fieldElemType).Elem()
if srcElemVal.Kind() == reflect.Map {
// Expect map[string]any
mIface, ok := srcElemVal.Interface().(map[string]any)View on GitHub (pinned to 1774ccb71a)