{"record":{"id":"29ba3461c99009f7","repo":"AlistGo/alist","slug":"cannot-scan-t-29ba34","errorCode":null,"errorMessage":"cannot scan %T","messagePattern":"cannot scan %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/model/roles.go","lineNumber":25,"sourceCode":")\n\ntype Roles []int\n\nfunc (r Roles) Value() (driver.Value, error) {\n\treturn json.Marshal([]int(r))\n}\n\nfunc (r *Roles) Scan(value interface{}) error {\n\tswitch v := value.(type) {\n\tcase []byte:\n\t\treturn json.Unmarshal(v, (*[]int)(r))\n\tcase string:\n\t\treturn json.Unmarshal([]byte(v), (*[]int)(r))\n\tcase nil:\n\t\t*r = nil\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot scan %T\", value)\n\t}\n}\n\nfunc (r Roles) Contains(role int) bool {\n\tfor _, v := range r {\n\t\tif v == role {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n","sourceCodeStart":7,"sourceCodeEnd":37,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/internal/model/roles.go#L7-L37","documentation":"Same Scanner-hook pattern as Paths.Scan but for the Roles type ([]int stored as JSON). (*Roles).Scan only accepts []byte, string, or nil; any other driver value type triggers 'cannot scan %T'. It is the deserialization boundary between the DB column and the []int role list.","triggerScenarios":"Loading a user row where the roles column is returned by the driver as a typed JSON value, a number, or another non-text type; the column contains data but the driver does not present it as bytes/string.","commonSituations":"Database backend change altering how JSON columns are scanned; Postgres JSONB via a driver that returns a non-[]byte representation; schema drifted after migrations; corrupted column content.","solutions":["Look at the '%T' in the message to see the concrete type your driver returns.","Keep the roles column as a text/JSON string type so Scan receives []byte or string.","Extend the type switch with the driver-specific type if needed.","Fix rows whose roles column is not a valid JSON array of integers."],"exampleFix":"// before\ncase string:\n    return json.Unmarshal([]byte(v), (*[]int)(r))\n\n// after\ncase string:\n    return json.Unmarshal([]byte(v), (*[]int)(r))\ncase json.RawMessage:\n    return json.Unmarshal(v, (*[]int)(r))","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"tv := reflect.TypeOf(value)\nif value != nil && tv != nil && tv.Kind() != reflect.String && tv.Kind() != reflect.Slice {\n    return fmt.Errorf(\"roles column must arrive as string or []byte, got %T\", value)\n}","tryCatchPattern":"if err := user.Roles.Scan(col); err != nil {\n    if strings.HasPrefix(err.Error(), \"cannot scan\") {\n        // inspect %T, fix column type or extend the switch\n    }\n}","preventionTips":["Store roles strictly as a JSON array of integers in a text column.","Cover Roles round-trip in DB integration tests per backend.","Extend the Scan switch when adopting a driver that returns typed JSON values."],"tags":["gorm","database","serialization","json","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}