semaphoreui/semaphore · error
expected object for map[string]interface
Error message
expected object for map[string]interface{}, got %T What it means
This branch handles fields of type db.MapStringAnyField during backup unmarshalling. If the JSON value for such a field is not a JSON object (map[string]any), the unmarshaller returns this error. It enforces that dynamic map fields in the backup are serialized as objects.
Solutions
- Edit the backup JSON so the field is a JSON object with string keys (e.g. {"key": "value"}).
- Check which struct field is db.MapStringAnyField and verify its serialized value matches an object shape.
- Re-export the backup from the original system to get correctly typed values.
- If the data truly is a list, change the model field type instead of the backup.
Example fix
// before
"meta": ["a", "b"]
// after
"meta": {"a": 1, "b": 2} Defensive patterns
Strategy: validation
Validate before calling
var raw map[string]json.RawMessage
json.Unmarshal(backupJSON, &raw)
for _, field := range mapStringAnyFields { // fields of type db.MapStringAnyField
var obj map[string]any
if err := json.Unmarshal(raw[field], &obj); err != nil {
return fmt.Errorf("field %q must be a JSON object", field)
}
} Type guard
func isJSONObject(v any) bool { _, ok := v.(map[string]any); return ok } Try / catch
if err := project.Unmarshal(data, &dst); err != nil {
if strings.Contains(err.Error(), "expected object for map[string]interface{}") {
// inspect the offending field in the backup JSON
}
return err
} Prevention
- Keep dynamic map fields serialized as JSON objects with string keys.
- Avoid tooling that reshapes objects into arrays during export/import.
- Validate backups against config.schema.yaml before restore.
When it happens
Trigger: project.Unmarshal on backup JSON where a db.MapStringAnyField field holds an array, string, number, bool, or null instead of a JSON object.
Common situations: Manual edits converting an object field to a list; tooling that flattens objects into arrays; schema drift after a field type changed between versions; copy-paste from a different entity that uses an array for the same field.
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
- expected array for slice, got %T
- expected object for map, got %T
- cannot set value
- unsupported kind
- expected bool for field, got %T
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/a5df2197a8fd6f72.
Report an issue: GitHub.
Appendix: source
Thrown at services/project/backup_marshal.go:224
if !ok {
return fmt.Errorf("expected array for slice, got %T", data)
}
slice := reflect.MakeSlice(v.Type(), len(dataSlice), len(dataSlice))
for i := 0; i < len(dataSlice); i++ {
elem := slice.Index(i)
if err := unmarshalValueWithBackupTags(dataSlice[i], elem); err != nil {
return err
}
}
v.Set(slice)
return nil
}
if v.Type() == reflect.TypeOf(db.MapStringAnyField{}) {
// Data should be a map
m, ok := data.(map[string]any)
if !ok {
return fmt.Errorf("expected object for map[string]interface{}, got %T", data)
}
v.Set(reflect.ValueOf(db.MapStringAnyField(m)))
return nil
}
// Handle maps
if v.Kind() == reflect.Map {
dataMap, ok := data.(map[string]any)
if !ok {
return fmt.Errorf("expected object for map, got %T", data)
}
mapType := v.Type()
mapValue := reflect.MakeMap(mapType)
for key, value := range dataMap {
keyVal := reflect.ValueOf(key).Convert(mapType.Key())
valVal := reflect.New(mapType.Elem()).Elem()
if err := unmarshalValueWithBackupTags(value, valVal); err != nil {
return errView on GitHub (pinned to 1774ccb71a)