Skip to content

redis.WithTimeout() causes pubSubPool nil pointer dereference panic #3662

@aeroplane

Description

@aeroplane

Simple:

newRdb.SubscribeHandler() reports "panic: nil pointer" after newRdb := redis.WithTimeout()
Because pubSubPool is not cloned


Sorry, English is not my native language, I wrote the details using AI.

Expected Behavior

A new Redis client instance created via WithTimeout() should retain the pubSubPool from the original client, allowing Pub/Sub operations (e.g., SubscribeHandler) to execute without memory errors.

Current Behavior

After calling WithTimeout() to clone a Redis client, using the new client for Pub/Sub operations triggers a nil pointer dereference panic. The error stack trace points to the pubSubPool field being nil in the cloned client:

panic: runtime error: invalid memory address or nil pointer dereference
go/pkg/mod/github.com/redis/go-redis/v9@v9.17.2/internal/pool/pubsub.go:37 +0x69

Root cause: The clone() method of baseClient does not copy the pubSubPool field to the new client instance.

Possible Solution

Modify the (c *baseClient) clone() *baseClient method in redis.go to explicitly clone the pubSubPool field from the original client to the new cloned instance.

Steps to Reproduce

  1. Initialize a base Redis client: rdb := redis.NewClient(...)
  2. Clone the client with a custom timeout: newRdb := rdb.WithTimeout(time.Second)
  3. Invoke Pub/Sub functionality on the cloned client: newRdb.SubscribeHandler(ctx, channel, handler)
  4. The program panics with the nil pointer dereference error mentioned above.

Context (Environment)

  • Affected Version: github.com/redis/go-redis/v9 v9.17.2
  • Problem Introduction: The issue emerged after the commit https://github.com/redis/go-redis/commit/0ef6d0727d6a452b0ea6eeee6bef3a72d35495ba#diff-190fc9ceda3bbdbe723d493b8b99e2b9a0100c7d635bfe9b04246280bef6f67e
  • Impact: Breaks Pub/Sub functionality for clients cloned via WithTimeout(), blocking related message subscription/publishing workflows.

Detailed Description

The baseClient struct in go-redis/v9 includes a pubSubPool field responsible for managing Pub/Sub connections. The WithTimeout() method relies on the clone() method to create a new client instance with updated timeout settings. However, the current implementation of clone() omits the pubSubPool field, leaving it as nil in the cloned client. When the cloned client attempts to perform Pub/Sub operations, it accesses the uninitialized pubSubPool, resulting in the nil pointer panic.

Possible Implementation

Update the clone() method of baseClient in redis.go to include the pubSubPool field during cloning:

func (c *baseClient) clone() *baseClient {
	c.maintNotificationsManagerLock.RLock()
	maintNotificationsManager := c.maintNotificationsManager
	c.maintNotificationsManagerLock.RUnlock()

	clone := &baseClient{
		opt:                         c.opt,
		connPool:                    c.connPool,
		onClose:                     c.onClose,
		pubSubPool:               c.pubSubPool, // Add this line to clone pubSubPool

		pushProcessor:               c.pushProcessor,
		maintNotificationsManager:   maintNotificationsManager,
		streamingCredentialsManager: c.streamingCredentialsManager,
	}
	return clone
    
}

This change ensures the cloned client inherits the pubSubPool from the original client, eliminating the nil pointer dereference error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions