semaphoreui/semaphore · error
expected slice or json array string for field
Error message
expected slice or json array string for field %s but got %T
What it means
For slice-typed struct fields, assignMapToStructRecursive accepts a slice/array, a JSON array string, or (for []string fields) a bare string treated as one element. If the value is a string that is neither valid JSON array nor the field's element type is string, this error is returned. It means the string form cannot be turned into the target slice.
Solutions
- Format the string as a valid JSON array, e.g. "[8080,8443]" instead of "8080,8443".
- Pass a real Go slice (e.g. []int{8080, 8443}) in the map instead of a string.
- If the field is []string, a bare string is allowed as a single element — check the field's element type.
- Split the string yourself and build the typed slice before calling AssignMapToStruct.
Example fix
// before
m := map[string]any{"ports": "8080,8443"} // field is []int
// after
m := map[string]any{"ports": []any{8080, 8443}} Defensive patterns
Strategy: validation
Validate before calling
var probe []any
if s, ok := m["ports"].(string); ok {
if err := json.Unmarshal([]byte(s), &probe); err != nil {
return fmt.Errorf("ports must be a JSON array string, got %q", s)
}
} Type guard
func isJSONArrayString(v any) bool {
s, ok := v.(string)
if !ok {
return false
}
var arr []any
return json.Unmarshal([]byte(s), &arr) == nil
} Try / catch
if err := util.AssignMapToStruct(m, &cfg); err != nil {
if strings.Contains(err.Error(), "expected slice or json array string") {
log.Fatalf("slice field %v needs JSON array format", err)
}
} Prevention
- Prefer real Go slices over JSON-encoded strings in source maps.
- Standardize on JSON arrays for list-shaped config in env vars and files.
- Document the expected element type of each slice config field.
When it happens
Trigger: Assigning a plain (non-JSON) string like "a,b,c" to a slice field whose element type is not string (e.g. []int, []time.Duration), or a malformed JSON array string to any slice field.
Common situations: Config values sourced from environment variables or files as comma-separated strings assigned to []int or []struct fields; a typo in a JSON array string (missing brackets); a numeric slice field receiving "8080,8443".
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- expected slice 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/0871bd94971a3974.
Report an issue: GitHub.
Appendix: source
Thrown at util/config.go:1195
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)
} 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()View on GitHub (pinned to 1774ccb71a)