{"record":{"id":"a5df2197a8fd6f72","repo":"semaphoreui/semaphore","slug":"expected-object-for-map-string-interface-got-t","errorCode":null,"errorMessage":"expected object for map[string]interface{}, got %T","messagePattern":"expected object for map\\[string\\]interface(.+?), got %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/project/backup_marshal.go","lineNumber":224,"sourceCode":"\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected array for slice, got %T\", data)\n\t\t}\n\t\tslice := reflect.MakeSlice(v.Type(), len(dataSlice), len(dataSlice))\n\t\tfor i := 0; i < len(dataSlice); i++ {\n\t\t\telem := slice.Index(i)\n\t\t\tif err := unmarshalValueWithBackupTags(dataSlice[i], elem); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tv.Set(slice)\n\t\treturn nil\n\t}\n\n\tif v.Type() == reflect.TypeOf(db.MapStringAnyField{}) {\n\t\t// Data should be a map\n\t\tm, ok := data.(map[string]any)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected object for map[string]interface{}, got %T\", data)\n\t\t}\n\t\tv.Set(reflect.ValueOf(db.MapStringAnyField(m)))\n\t\treturn nil\n\t}\n\n\t// Handle maps\n\tif v.Kind() == reflect.Map {\n\t\tdataMap, ok := data.(map[string]any)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected object for map, got %T\", data)\n\t\t}\n\t\tmapType := v.Type()\n\t\tmapValue := reflect.MakeMap(mapType)\n\t\tfor key, value := range dataMap {\n\t\t\tkeyVal := reflect.ValueOf(key).Convert(mapType.Key())\n\t\t\tvalVal := reflect.New(mapType.Elem()).Elem()\n\t\t\tif err := unmarshalValueWithBackupTags(value, valVal); err != nil {\n\t\t\t\treturn err","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/services/project/backup_marshal.go#L206-L242","documentation":"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.","triggerScenarios":"project.Unmarshal on backup JSON where a db.MapStringAnyField field holds an array, string, number, bool, or null instead of a JSON object.","commonSituations":"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.","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."],"exampleFix":"// before\n\"meta\": [\"a\", \"b\"]\n// after\n\"meta\": {\"a\": 1, \"b\": 2}","handlingStrategy":"validation","validationCode":"var raw map[string]json.RawMessage\njson.Unmarshal(backupJSON, &raw)\nfor _, field := range mapStringAnyFields { // fields of type db.MapStringAnyField\n    var obj map[string]any\n    if err := json.Unmarshal(raw[field], &obj); err != nil {\n        return fmt.Errorf(\"field %q must be a JSON object\", field)\n    }\n}","typeGuard":"func isJSONObject(v any) bool { _, ok := v.(map[string]any); return ok }","tryCatchPattern":"if err := project.Unmarshal(data, &dst); err != nil {\n    if strings.Contains(err.Error(), \"expected object for map[string]interface{}\") {\n        // inspect the offending field in the backup JSON\n    }\n    return err\n}","preventionTips":["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."],"tags":["go","reflection","unmarshal","backup-restore"],"backgroundTag":"incompatible-source-type","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}