ory/hydra · error

cannot scan %T into region.Region

Error message

cannot scan %T into region.Region

What it means

Region implements sql.Scanner; Scan only accepts string, []byte, and nil (empty string) values. When the driver yields any other Go type — most commonly int, int64, or time.Time from a non-text column — the scan is rejected with this error. It signals that the DB column behind Region is not text-typed.

Source

Thrown at oryx/region/region.go:96

		return other == AsiaNorthEast
	case US:
		return other == USEast || other == USWest
	}
	return false
}

// Scan implements sql.Scanner. NULL and empty scan to the zero value;
// validate via Region.Valid if "unset" must be rejected.
func (r *Region) Scan(src any) error {
	switch s := src.(type) {
	case nil:
		*r = ""
	case string:
		*r = Region(s)
	case []byte:
		*r = Region(s)
	default:
		return errors.Errorf("cannot scan %T into region.Region", src)
	}
	return nil
}

// Value implements driver.Valuer. The empty Region writes as "".
func (r Region) Value() (driver.Value, error) {
	return string(r), nil
}

// IsEqual compares two nullable *Region pointers (both nil = equal).
func IsEqual(a, b *Region) bool {
	if a == nil && b == nil {
		return true
	}
	if a == nil || b == nil {
		return false
	}
	return *a == *b

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Change the column type to TEXT/VARCHAR in the schema (via a migration) so the driver yields string/[]byte.
  2. Cast in SQL: SELECT region_id::text FROM ... so the driver returns a string.
  3. Scan into a native type (int/uuid) first, then convert to region.Region in Go before assignment.
  4. Check the driver: ensure it returns string/[]byte for the column (some drivers return native typed values for certain column types).

Example fix

// before
rows.Scan(&region) // region is region.Region; column is INT -> error

// after
rows.Scan("SELECT region_id::text") // or:
var id int64
rows.Scan(&id)
region = region.Region(strconv.FormatInt(id, 10))
Defensive patterns

Strategy: type-guard

Validate before calling

// verify the column is text-typed before scanning
var dataType string
err := db.QueryRow(`SELECT data_type FROM information_schema.columns
  WHERE table_name=$1 AND column_name=$2`, "locations", "region").Scan(&dataType)
// dataType must be "text", "character varying", or similar

Type guard

func scannableIntoRegion(v any) bool {
    switch v.(type) {
    case nil, string, []byte:
        return true
    default:
        return false
    }
}
// check scannableIntoRegion(raw) before assigning to Region

Try / catch

var r region.Region
if err := rows.Scan(&r); err != nil {
    if strings.Contains(err.Error(), "cannot scan") {
        // column is not text; scan into native type and convert
        var raw any
        _ = rows.Scan(&raw)
        return fmt.Errorf("region column has non-text type %T: %w", raw, err)
    }
    return err
}

Prevention

When it happens

Trigger: SELECTing/scanning a column whose driver type is numeric, enum-as-int, UUID object, or time into a *Region field; using a driver that returns non-string types for text columns (rare); raw queries that SELECT non-text expressions (e.g. region_id integer) into a Region model field.

Common situations: Schema drift: a column that used to be TEXT/VARCHAR was migrated to an integer enum; switching drivers (e.g. Postgres numeric or a UUID extension returning driver-specific types); copying a raw row value into Region via database/sql Scan.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/f4dbc0067a1681f6. Report an issue: GitHub.