hibiken/asynq · warning

redis connection is shared so the Inspector can't be closed…

Error message

redis connection is shared so the Inspector can't be closed through asynq

What it means

Inspector.Close refuses to close a Redis connection that the Inspector shares with another client (created via NewInspectorFromRedisClient or similar sharing constructors). Closing the shared connection would break the other asynq components using it, so the library returns this error instead of closing. The caller owns the connection in that case and must close it themselves.

Solutions

  1. Track connection ownership yourself: call Close on the original redis client you passed in, not on the shared Inspector.
  2. Construct the Inspector with its own connection (asynq.NewInspector(redisAddr)) if you need Inspector.Close to work.
  3. Guard shutdown code: skip inspector.Close() when the Inspector was built from a shared client.
  4. Log and ignore this specific error during teardown if closing the shared connection elsewhere.

Example fix

// before
inspector := asynq.NewInspectorFromRedisClient(rdb)
defer inspector.Close() // errors: connection is shared
// after
inspector := asynq.NewInspectorFromRedisClient(rdb)
defer func() {
    if err := inspector.Close(); err != nil {
        log.Println("inspector uses shared connection; closing owner rdb instead")
    }
    rdb.Close()
}()
Defensive patterns

Strategy: try-catch

Validate before calling

// track how the inspector was created
func closableInspector(shared bool, i *asynq.Inspector) bool { return !shared }

Try / catch

if err := inspector.Close(); err != nil && strings.Contains(err.Error(), "connection is shared") {
    // close the owning redis client instead
    rdb.Close()
}

Prevention

When it happens

Trigger: Calling Inspector.Close() on an Inspector constructed from a shared redis.UniversalClient — e.g. tests or apps that pass one redis client to both asynq.Client and asynq.NewInspectorFromRedisClient, then call inspector.Close().

Common situations: Sharing a single redis.Client across Client, Scheduler, and Inspector for connection pooling; test cleanup/deferred Close on a shared-connection Inspector; embedding the Inspector in a service that closes all resources on shutdown.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07). Data as JSON: /api/errors/a7b8bba2b3ff96d0. Report an issue: GitHub.

Appendix: source

Thrown at inspector.go:52

	}
	inspector := NewInspectorFromRedisClient(c)
	inspector.sharedConnection = false
	return inspector
}

// NewInspectorFromRedisClient returns a new instance of Inspector given a redis.UniversalClient
// Warning: The underlying redis connection pool will not be closed by Asynq, you are responsible for closing it.
func NewInspectorFromRedisClient(c redis.UniversalClient) *Inspector {
	return &Inspector{
		rdb:              rdb.NewRDB(c),
		sharedConnection: true,
	}
}

// Close closes the connection with redis.
func (i *Inspector) Close() error {
	if i.sharedConnection {
		return fmt.Errorf("redis connection is shared so the Inspector can't be closed through asynq")
	}
	return i.rdb.Close()
}

// Queues returns a list of all queue names.
func (i *Inspector) Queues() ([]string, error) {
	return i.rdb.AllQueues()
}

// Groups returns a list of all groups within the given queue.
func (i *Inspector) Groups(queue string) ([]*GroupInfo, error) {
	stats, err := i.rdb.GroupStats(queue)
	if err != nil {
		return nil, err
	}
	var res []*GroupInfo
	for _, s := range stats {
		res = append(res, &GroupInfo{

View on GitHub (pinned to d135f1439b)