redis/go-redis · error

redis.Scan(non-pointer %T)

Error message

redis.Scan(non-pointer %T)

What it means

hscan.Struct (exposed as redis.Struct, used by HGETALL-based struct scanning) requires the destination to be a non-nil pointer to a struct. If dst is not a pointer or is a nil pointer, Struct returns 'redis.Scan(non-pointer %T)'. This is a caller-side type misuse detected via reflection before any Redis data is touched.

Source

Thrown at internal/hscan/hscan.go:60

		reflect.Ptr:           decodeUnsupported,
		reflect.Slice:         decodeSlice,
		reflect.String:        decodeString,
		reflect.Struct:        decodeUnsupported,
		reflect.UnsafePointer: decodeUnsupported,
	}

	// Global map of struct field specs that is populated once for every new
	// struct type that is scanned. This caches the field types and the corresponding
	// decoder functions to avoid iterating through struct fields on subsequent scans.
	globalStructMap = newStructMap()
)

func Struct(dst interface{}) (StructValue, error) {
	v := reflect.ValueOf(dst)

	// The destination to scan into should be a struct pointer.
	if v.Kind() != reflect.Ptr || v.IsNil() {
		return StructValue{}, fmt.Errorf("redis.Scan(non-pointer %T)", dst)
	}

	v = v.Elem()
	if v.Kind() != reflect.Struct {
		return StructValue{}, fmt.Errorf("redis.Scan(non-struct %T)", dst)
	}

	return StructValue{
		spec:  globalStructMap.get(v.Type()),
		value: v,
	}, nil
}

// Scan scans the results from a key-value Redis map result set to a destination struct.
// The Redis keys are matched to the struct's field with the `redis` tag.
func Scan(dst interface{}, keys []interface{}, vals []interface{}) error {
	if len(keys) != len(vals) {
		return errors.New("args should have the same number of keys and vals")

View on GitHub (pinned to c5cad058c7)

Solutions

  1. Pass a pointer to a struct: redis.Scan(&user)
  2. Ensure the pointer is non-nil and initialized
  3. Verify the pointed-to type is a struct, not a map/slice
  4. When dst is interface{}, assert it holds a *T before calling Scan

Example fix

// before
var user User
res, err := redis.Scan(user) // redis.Scan(non-pointer hscan.User)
// after
var user User
res, err := redis.Scan(&user)
Defensive patterns

Strategy: validation

Validate before calling

func validScanDst(dst interface{}) bool {
	v := reflect.ValueOf(dst)
	return v.Kind() == reflect.Ptr && !v.IsNil() && v.Elem().Kind() == reflect.Struct
}

Try / catch

res, err := redis.Scan(dst)
if err != nil {
	if strings.Contains(err.Error(), "non-pointer") {
		return fmt.Errorf("Scan requires &struct: %w", err)
	}
}

Prevention

When it happens

Trigger: redis.Scan(myStruct) instead of redis.Scan(&myStruct); passing a nil typed pointer (*MyStruct)(nil); passing a map or slice address where a struct pointer is required.

Common situations: Copying examples and forgetting the &; storing the destination in an interface{} that holds a non-pointer; scanning into a map[string]string instead of a struct.

Related errors


AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01). Data as JSON: /api/errors/d5340911a8492d2f. Report an issue: GitHub.