ory/hydra · error

cannot scan %#v into StringSliceJSONFormat

Error message

cannot scan %#v into StringSliceJSONFormat

What it means

StringSliceJSONFormat implements sql.Scanner for a []string stored as JSON. Its Scan accepts nil, string, and []byte only; any other driver value (int, float64, time.Time, bool, etc.) is rejected with this error. It indicates the source column is not a text/JSON column carrying a JSON array document.

Source

Thrown at oryx/sqlxx/types.go:61

}

// StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.
// swagger:type array
type StringSliceJSONFormat []string

// Scan implements the Scanner interface.
func (m *StringSliceJSONFormat) Scan(value interface{}) error {
	var val string
	switch v := value.(type) {
	case nil:
		*m = StringSliceJSONFormat{}
		return nil
	case string:
		val = v
	case []byte:
		val = string(v)
	default:
		return errors.Errorf("cannot scan %#v into StringSliceJSONFormat", value)
	}
	if len(val) == 0 {
		val = "[]"
	}

	if parsed := gjson.Parse(val); parsed.Type == gjson.Null {
		val = "[]"
	} else if !parsed.IsArray() {
		return errors.Errorf("expected JSON value to be an array but got type: %s", parsed.Type.String())
	}

	return errors.WithStack(json.Unmarshal([]byte(val), &m))
}

// Value implements the driver Valuer interface.
func (m StringSliceJSONFormat) Value() (driver.Value, error) {
	if len(m) == 0 {
		return "[]", nil

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Change the column to TEXT or JSON/JSONB storing a JSON array (e.g. '["a","b"]') via a migration.
  2. Cast in SQL: SELECT col::text FROM ... so the driver delivers a string/[]byte.
  3. Scan into the driver's native Go type first, marshal it to a JSON array, then assign to StringSliceJSONFormat.
  4. If the column truly holds scalars, change the model field type — StringSliceJSONFormat is only for arrays.

Example fix

// before
rows.Scan(&tags) // tags is sqlxx.StringSliceJSONFormat; column is INT -> error

// after
rows.Scan("SELECT tags::text")
// or migrate column: ALTER TABLE items ALTER COLUMN tags TYPE jsonb USING ...;
Defensive patterns

Strategy: type-guard

Validate before calling

var dataType string
err := db.QueryRow(`SELECT data_type FROM information_schema.columns
  WHERE table_name=$1 AND column_name=$2`, "items", "tags").Scan(&dataType)
// acceptable: "text", "character varying", "json", "jsonb"

Type guard

func scannableIntoStringSlice(v any) bool {
    switch v.(type) {
    case nil, string, []byte:
        return true
    default:
        return false
    }
}

Try / catch

var tags sqlxx.StringSliceJSONFormat
if err := rows.Scan(&tags); err != nil {
    if strings.Contains(err.Error(), "cannot scan") {
        // column returned a non-text driver type; fetch as text or fix column
        return fmt.Errorf("tags column is not text/json: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Scanning a column of numeric, boolean, timestamp, or native-JSON-object-driver type into a *StringSliceJSONFormat field; raw queries returning non-text expressions into that field; a driver that decodes JSONB into non-string types.

Common situations: Model field changed from a string slice to a scalar but the Scan type was left in place; column switched from TEXT/JSONB to BIGINT; migrating data where an old column held CSV/integer ids now being read into the JSON-based type.

Related errors


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