{"record":{"id":"2dcba8da9700a82a","repo":"jmoiron/sqlx","slug":"sql-rawbytes-isn-t-allowed-on-row-scan","errorCode":null,"errorMessage":"sql: RawBytes isn't allowed on Row.Scan","messagePattern":"sql: RawBytes isn't allowed on Row\\.Scan","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sqlx.go","lineNumber":198,"sourceCode":"\t}\n\n\t// TODO(bradfitz): for now we need to defensively clone all\n\t// []byte that the driver returned (not permitting\n\t// *RawBytes in Rows.Scan), since we're about to close\n\t// the Rows in our defer, when we return from this function.\n\t// the contract with the driver.Next(...) interface is that it\n\t// can return slices into read-only temporary memory that's\n\t// only valid until the next Scan/Close.  But the TODO is that\n\t// for a lot of drivers, this copy will be unnecessary.  We\n\t// should provide an optional interface for drivers to\n\t// implement to say, \"don't worry, the []bytes that I return\n\t// from Next will not be modified again.\" (for instance, if\n\t// they were obtained from the network anyway) But for now we\n\t// don't care.\n\tdefer r.rows.Close()\n\tfor _, dp := range dest {\n\t\tif _, ok := dp.(*sql.RawBytes); ok {\n\t\t\treturn errors.New(\"sql: RawBytes isn't allowed on Row.Scan\")\n\t\t}\n\t}\n\n\tif !r.rows.Next() {\n\t\tif err := r.rows.Err(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn sql.ErrNoRows\n\t}\n\terr := r.rows.Scan(dest...)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Make sure the query can be processed to completion with no errors.\n\tif err := r.rows.Close(); err != nil {\n\t\treturn err\n\t}\n\treturn nil","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/jmoiron/sqlx/blob/41dac167fdad5e3fd81d66cafba0951dc6823a30/sqlx.go#L180-L216","documentation":"Row.Scan (sqlx's wrapper) rejects *sql.RawBytes destinations because database/sql forbids RawBytes in single-row Scan: RawBytes points into memory owned by the driver that is only valid between calls to Rows.Next, and Row.Scan closes the rows immediately after scanning, so the bytes would dangle. sqlx enforces the same rule up-front with this explicit error.","triggerScenarios":"Calling row.StructScan/scanAny (via Get) with a destination struct or slice containing a *sql.RawBytes field, e.g. db.Get(&s, \"SELECT blob FROM t\") where s has a RawBytes field.","commonSituations":"Optimizing scans of large blob/bytea columns with RawBytes after reading database/sql docs; shared struct types used both in Rows scanning (where RawBytes is legal) and single-row Get (where it is not).","solutions":["Change the field to []byte or sql.NullString instead of *sql.RawBytes","Query with db.QueryRow(...).Scan into a []byte and assign it to the struct manually","Keep two struct types: one with RawBytes for Rows-based scanning, one with []byte for Get","Load the blob with a plain Query and scan from the live Rows before Close"],"exampleFix":"// before\ntype Doc struct { Body *sql.RawBytes }\nrow := db.QueryRowx(\"SELECT body FROM docs WHERE id=$1\", id)\nrow.StructScan(&doc) // panics/errors\n// after\ntype Doc struct { Body []byte }\nrow := db.QueryRowx(\"SELECT body FROM docs WHERE id=$1\", id)\nrow.StructScan(&doc)","handlingStrategy":"type-guard","validationCode":"func checkNoRawBytes(dest interface{}) error {\n    v := reflect.Indirect(reflect.ValueOf(dest))\n    if v.Kind() == reflect.Struct {\n        for i := 0; i < v.NumField(); i++ {\n            if v.Field(i).Type() == reflect.TypeOf((*sql.RawBytes)(nil)) {\n                return fmt.Errorf(\"field %s is *sql.RawBytes; not allowed in Row.Scan\", v.Type().Field(i).Name)\n            }\n        }\n    }\n    return nil\n}","typeGuard":"func isRawBytesField(f reflect.StructField) bool {\n    return f.Type == reflect.TypeOf((*sql.RawBytes)(nil))\n}","tryCatchPattern":"err := row.StructScan(&doc)\nif err != nil {\n    if err.Error() == \"sql: RawBytes isn't allowed on Row.Scan\" {\n        return fmt.Errorf(\"replace RawBytes field with []byte: %w\", err)\n    }\n    return err\n}","preventionTips":["Use []byte or sql.NullString instead of *sql.RawBytes in structs scanned via Get/StructScan on Row","Reserve RawBytes for Rows.Next loops only","Document struct types' allowed scan contexts"],"tags":["go","sqlx","rawbytes","row-scan"],"backgroundTag":"rawbytes-not-allowed","analyzedSha":"41dac167fdad5e3fd81d66cafba0951dc6823a30","analyzedAt":"2026-09-03T06:10:20.382Z","contentChangedAt":"2026-09-03T06:10:20.382Z","schemaVersion":2},"datasetVersion":"2026-09-10T12:17:11.382Z"}