ory/hydra · error
unable to scan type %T as JSON into %T
Error message
unable to scan type %T as JSON into %T
What it means
JSONScan scans a database value (string, []byte, or null) into a destination pointer via json.Unmarshal. It throws this error when the value passed by the database driver is of an unsupported Go type (e.g. int, time.Time, []interface{}), because it cannot convert it to raw JSON text to unmarshal. It indicates a driver/type mismatch rather than invalid JSON.
Source
Thrown at oryx/sqlxx/types.go:501
// UnmarshalJSON sets *m to a copy of data.
func (m *NullJSONObject) UnmarshalJSON(data []byte) error {
return (*NullJSONRawMessage)(m).UnmarshalJSON(data)
}
// JSONScan is a generic helper for retrieving a SQL JSON-encoded value.
func JSONScan(dst, value any) error {
// Note: raw is a string (not []byte) because the MySQL driver reuses byte slices across scans.
// Using strings avoids the need to manually copy the byte slice.
var raw string
switch v := value.(type) {
case nil:
raw = "null"
case string:
raw = v
case []byte:
raw = string(v)
default:
return fmt.Errorf("unable to scan type %T as JSON into %T", value, dst)
}
if err := json.Unmarshal([]byte(raw), dst); err != nil {
return fmt.Errorf("unable to decode JSON payload into %T: %w", dst, err)
}
return nil
}
// NullInt64 represents an int64 that may be null.
// swagger:type int64
// swagger:model nullInt64
type NullInt64 struct {
Int int64
Valid bool // Valid is true if Duration is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullInt64) Scan(value interface{}) error {
d := sql.NullInt64{}View on GitHub (pinned to 4174065ffb)
Solutions
- Check the actual Go type the driver returns for the column (log %T of the value) and pick a driver setting that returns strings/[]byte for JSON columns
- Ensure the DB column is a text/JSON type that the driver delivers as string or []byte
- Implement a custom Scan on the destination type that handles the driver's concrete type (e.g. convert numbers/bools with json.Marshal before unmarshal)
- Do not bind JSONScan-backed types to columns that are not JSON/text
Example fix
// before: driver returns map for JSONB column
func (j *JSONB) Scan(value interface{}) error { return JSONScan(value, j) }
// after: normalize unsupported types first
func (j *JSONB) Scan(value interface{}) error {
switch v := value.(type) {
case []byte, string, nil:
return JSONScan(value, j)
default:
b, err := json.Marshal(value)
if err != nil { return err }
return JSONScan(b, j)
}
} Defensive patterns
Strategy: validation
Validate before calling
func canJSONScan(value interface{}) bool {
switch value.(type) {
case nil, string, []byte:
return true
default:
return false
}
} Type guard
func isJSONScanable(v interface{}) bool {
switch v.(type) {
case nil, string, []byte:
return true
}
return false
} Prevention
- Log %T of driver values for JSON columns to know what the driver returns
- Configure the driver to return JSON columns as string/[]byte
- Only bind JSONScan-backed types to text/JSON columns
When it happens
Trigger: Scanning a column whose driver returns a native non-string type (e.g. a JSONB returned as a parsed value, an int, or time.Time) into a field whose Scan delegates to JSONScan (e.g. a JSON/JSONB or nullable JSON column mapped to a custom type wrapping this helper).
Common situations: Using a driver that decodes JSON columns into map[string]interface{} instead of []byte (e.g. certain pq/pgx or sqlite drivers), storing JSON in a non-text column type, or ORM scanning a numeric/boolean column into a JSON-typed struct field.
Related errors
- cannot scan %#v into StringSliceJSONFormat
- expected JSON value to be an array but got type: %s
- cannot scan %T into region.Region
- cookiex: payload must be a flat JSON object with string valu
- unable to decode JSON payload into %T: %w
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/8b44caf60172ab37.
Report an issue: GitHub.