go-sql-driver/mysql · error

non-Value type %T returned from Value

Error message

non-Value type %T returned from Value

What it means

In the driver's converter (statement.go:180), a type implementing driver.Valuer returned a value from its Value() method that is not a valid driver.Value (nil, int64, float64, bool, []byte, string, time.Time) nor uint64 (a driver-specific allowance). The contract of driver.Valuer.Value() is to return one of those base types; returning anything else is a bug in the Valuer implementation.

Source

Thrown at statement.go:180

	if driver.IsValue(v) {
		return v, nil
	}

	if vr, ok := v.(driver.Valuer); ok {
		sv, err := callValuerValue(vr)
		if err != nil {
			return nil, err
		}
		if driver.IsValue(sv) {
			return sv, nil
		}
		// A value returned from the Valuer interface can be "a type handled by
		// a database driver's NamedValueChecker interface" so we should accept
		// uint64 here as well.
		if u, ok := sv.(uint64); ok {
			return u, nil
		}
		return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
	}
	rv := reflect.ValueOf(v)
	switch rv.Kind() {
	case reflect.Ptr:
		// indirect pointers
		if rv.IsNil() {
			return nil, nil
		} else {
			return c.ConvertValue(rv.Elem().Interface())
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return rv.Int(), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return rv.Uint(), nil
	case reflect.Float32, reflect.Float64:
		return rv.Float(), nil
	case reflect.Bool:
		return rv.Bool(), nil

View on GitHub (pinned to c426bd9379)

Solutions

  1. Make Value() return one of the allowed base types, most commonly int64, float64, string, []byte, or time.Time.
  2. Convert numeric custom types explicitly, e.g. return int64(m), nil.
  3. Return nil, nil to represent SQL NULL.

Example fix

// before
func (m Money) Value() (driver.Value, error) { return int(m), nil }

// after
func (m Money) Value() (driver.Value, error) { return int64(m), nil }
Defensive patterns

Strategy: type-guard

Validate before calling

func checkValuer(v driver.Valuer) error {
    val, err := v.Value()
    if err != nil { return err }
    switch val.(type) {
    case nil, int64, uint64, float64, bool, []byte, string, time.Time:
        return nil
    }
    return fmt.Errorf("Value() returned unsupported type %T", val)
}

Type guard

func isDriverValue(v any) bool {
    switch v.(type) {
    case nil, int64, uint64, float64, bool, []byte, string, time.Time:
        return true
    }
    return false
}

Try / catch

if _, err := stmt.Exec(ctx, customVal); err != nil {
    if strings.Contains(err.Error(), "non-Value type") {
        // fix the custom type's Value() to return a base driver.Value
    }
}

Prevention

When it happens

Trigger: A custom type's Value() returns a plain int (not int64), a float32, a struct, a *big.Int, or any non-base wrapper type; a Valuer that returns another custom type instead of unwrapping it.

Common situations: Implementing Value() to return int/uint/float32 directly; returning a nested wrapper type; misunderstanding the driver.Value type set.

Related errors


AI-assisted analysis of go-sql-driver/mysql@c426bd9379 (2026-08-04). Data as JSON: /data/errors/64cbfbab1d8030f0.json. Report an issue: GitHub.