{"record":{"id":"03aeb806ba7c7a57","repo":"semaphoreui/semaphore","slug":"expected-string-for-field-got-t","errorCode":null,"errorMessage":"expected string for field, got %T","messagePattern":"expected string for field, got %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/project/backup_marshal.go","lineNumber":125,"sourceCode":"\treturn v.Interface(), nil\n}\n\nfunc setBasicType(data any, v reflect.Value) error {\n\tif !v.CanSet() {\n\t\treturn fmt.Errorf(\"cannot set value\")\n\t}\n\n\tswitch v.Kind() {\n\tcase reflect.Bool:\n\t\tb, ok := data.(bool)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected bool for field, got %T\", data)\n\t\t}\n\t\tv.SetBool(b)\n\tcase reflect.String:\n\t\ts, ok := data.(string)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected string for field, got %T\", data)\n\t\t}\n\t\tv.SetString(s)\n\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\tn, ok := toFloat64(data)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected number for field, got %T\", data)\n\t\t}\n\t\tv.SetInt(int64(n))\n\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\tn, ok := toFloat64(data)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected number for field, got %T\", data)\n\t\t}\n\t\tv.SetUint(uint64(n))\n\tcase reflect.Float32, reflect.Float64:\n\t\tn, ok := toFloat64(data)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected number for field, got %T\", data)","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/services/project/backup_marshal.go#L107-L143","documentation":"In setBasicType (services/project/backup_marshal.go:125), when the destination field kind is String, the incoming data must be a JSON string. Any other Go type (number, bool, object, null) yields \"expected string for field, got %T\". This guards reflect.SetString against invalid types.","triggerScenarios":"unmarshalValueWithBackupTags maps a backup JSON value of non-string type onto a struct field whose kind is String - e.g. a number where a string is expected.","commonSituations":"Backup JSON where a string field (e.g. an ID stored as string) was serialized as a number; hand-edited backups; schema drift between the exporting and importing versions where a field changed type from number to string.","solutions":["Quote the value in the backup JSON so it is a proper string (e.g. 123 -> \"123\").","Change the struct field to a numeric type if the data is legitimately a number.","Pre-validate the backup file's field types against the expected schema before restoring.","Re-export the backup using a version whose schema matches the importing code."],"exampleFix":"// before (backup.json)\n\"project_id\": 5\n// after (field is a string type)\n\"project_id\": \"5\"","handlingStrategy":"validation","validationCode":"if v, ok := raw[\"name\"]; ok {\n    if _, ok := v.(string); !ok { return fmt.Errorf(\"field name must be string, got %T\", v) }\n}","typeGuard":"func isString(v any) bool { _, ok := v.(string); return ok }","tryCatchPattern":"if err := project.Unmarshal(data, &target); err != nil {\n    if strings.Contains(err.Error(), \"expected string for field\") {\n        return fmt.Errorf(\"backup schema mismatch: %w\", err)\n    }\n    return err\n}","preventionTips":["Quote string values in backup JSON even when they look numeric.","Validate field types before restore.","Match exporter and importer schema versions."],"tags":["go","json","unmarshal","type-mismatch","string"],"backgroundTag":"type-mismatch","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"}