{"record":{"id":"7de7f7916c28906b","repo":"semaphoreui/semaphore","slug":"expected-array-for-slice-got-t","errorCode":null,"errorMessage":"expected array for slice, got %T","messagePattern":"expected array for slice, got %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/project/backup_marshal.go","lineNumber":207,"sourceCode":"\t\t}\n\t\treturn unmarshalValueWithBackupTags(data, v.Elem())\n\t}\n\n\t// Handle structs\n\tif v.Kind() == reflect.Struct {\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 struct, got %T\", data)\n\t\t}\n\t\treturn unmarshalStructWithBackupTags(m, v)\n\t}\n\n\t// Handle slices and arrays\n\tif v.Kind() == reflect.Slice {\n\t\tdataSlice, ok := data.([]any)\n\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}","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/services/project/backup_marshal.go#L189-L225","documentation":"unmarshalValueWithBackupTags decodes backup JSON into Go values via reflection. When the destination is a slice (reflect.Slice) but the decoded JSON value is not []any (e.g. an object, string, or number), it returns this error. It signals a shape mismatch between the backup file and the target struct field tagged with backup tags.","triggerScenarios":"Calling project.Unmarshal (or unmarshalValueWithBackupTags directly) on backup JSON where a field whose Go type is a slice was serialized as a non-array JSON value (object, string, number, bool, or null path falling through to this branch).","commonSituations":"Hand-edited or externally generated backup files where an array field was replaced by an object or scalar; schema drift between Semaphore versions where a field changed from slice to map; partial JSON merges that swapped the value type.","solutions":["Inspect the backup JSON at the failing field and make it a JSON array (e.g. change {\"k\":v} or \"x\" to [...]).","Ensure the Go struct field type matches the JSON shape; if the field is now an object, change the struct field to a map or struct.","Regenerate the backup from a matching version of the source system instead of editing it manually.","Pre-validate the JSON shape with encoding/json unmarshalling into the target struct before calling Unmarshal to get precise paths."],"exampleFix":"// before (backup.json)\n\"users\": {\"name\": \"admin\"}\n// after\n\"users\": [{\"name\": \"admin\"}]","handlingStrategy":"validation","validationCode":"var raw map[string]json.RawMessage\nif err := json.Unmarshal(backupJSON, &raw); err != nil { return err }\nfor field, v := range raw {\n    var arr []any\n    if needsSlice(field) && json.Unmarshal(v, &arr) != nil {\n        return fmt.Errorf(\"field %q must be a JSON array, got %s\", field, string(v))\n    }\n}","typeGuard":"func isJSONArray(v any) bool { _, ok := v.([]any); return ok }","tryCatchPattern":"if err := project.Unmarshal(data, &dst); err != nil {\n    if strings.Contains(err.Error(), \"expected array for slice\") {\n        // log offending field, fix backup JSON or regenerate backup\n    }\n    return err\n}","preventionTips":["Never hand-edit backup JSON; always regenerate from the source system.","Keep backup producer and consumer on matching schema versions.","Run a JSON-schema validation pass over backup files before unmarshalling."],"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"}