Hey! 👋 Following up with another one we hit in production.
redis() decides whether it was handed a live connection or a config object with instanceof:
export function redis(config?: RedisConfig) {
return () => {
if (config instanceof Redis) {
return new RedisAdapter(config, false)
}
const options: RedisOptions = { host: 'localhost', port: 6379, keyPrefix: 'boringnode::queue::', db: 0, ...config }
const connection = new Redis(options)
return new RedisAdapter(connection, true)
}
}
ioredis is an optional peer dependency, so under pnpm the copy @boringnode/queue resolves is routinely not the copy the caller instantiated from. instanceof is then false for a perfectly valid connection, and the else-branch takes over. Spreading a Redis instance contributes no host/port/db/password, so the passed connection's settings are all silently discarded and the queue connects to localhost:6379, db 0, unauthenticated.
This hits @adonisjs/queue directly — its documented contract is "the Redis adapter uses your @adonisjs/redis connection", and it implements that as redisAdapter(connection.ioConnection).
What it looks like in practice
Our test env sets REDIS_DB=1 to isolate itself from the dev queue. The Redis service honoured it; the queue did not. Test runs enqueued real jobs into the dev queue on db 0, the dev worker picked them up after the test DB had been torn down, and we got a flood of "row no longer exists" job failures. CLIENT LIST showed the split plainly — lib-ver=5.11.1 (the app's connection) on db 1, lib-ver=5.9.1 (boringnode's own) on db 0.
The worse case is production: a deployment configuring REDIS_HOST/REDIS_PASSWORD gets a queue quietly pointed at localhost with no auth, rather than an error.
Repro
pnpm workspace, @adonisjs/queue + @adonisjs/redis, config/redis.ts with db: 1, config/queue.ts with drivers.redis({ connectionName: 'main' }). Dispatch any job → keys land in db 0. Confirmed by module resolution:
@adonisjs/redis → ioredis@5.11.1/built/index.js
@boringnode/queue → ioredis@5.9.1/built/index.js // different class, instanceof === false
Suggested fix
Duck-type instead of instanceof — it can't false-negative across package copies:
if (config && typeof (config as any).defineCommand === 'function') {
return new RedisAdapter(config as Redis, false)
}
Failing that, please make the fallback throw on an unrecognised object rather than silently substituting defaults — the silence is what made this expensive to find.
Versions
Reproduced on both @boringnode/queue@0.5.2 (via @adonisjs/queue@0.6.1) and @boringnode/queue@0.6.0 (via @adonisjs/queue@0.6.2) — same result, jobs land in db 0 either way. @adonisjs/redis@10.0.0, pnpm 11.15, Node 24. The check is unchanged on main.
Hey! 👋 Following up with another one we hit in production.
redis()decides whether it was handed a live connection or a config object withinstanceof:ioredisis an optional peer dependency, so under pnpm the copy@boringnode/queueresolves is routinely not the copy the caller instantiated from.instanceofis then false for a perfectly valid connection, and the else-branch takes over. Spreading aRedisinstance contributes nohost/port/db/password, so the passed connection's settings are all silently discarded and the queue connects tolocalhost:6379, db 0, unauthenticated.This hits
@adonisjs/queuedirectly — its documented contract is "the Redis adapter uses your@adonisjs/redisconnection", and it implements that asredisAdapter(connection.ioConnection).What it looks like in practice
Our test env sets
REDIS_DB=1to isolate itself from the dev queue. The Redis service honoured it; the queue did not. Test runs enqueued real jobs into the dev queue on db 0, the dev worker picked them up after the test DB had been torn down, and we got a flood of "row no longer exists" job failures.CLIENT LISTshowed the split plainly —lib-ver=5.11.1(the app's connection) on db 1,lib-ver=5.9.1(boringnode's own) on db 0.The worse case is production: a deployment configuring
REDIS_HOST/REDIS_PASSWORDgets a queue quietly pointed at localhost with no auth, rather than an error.Repro
pnpm workspace,
@adonisjs/queue+@adonisjs/redis,config/redis.tswithdb: 1,config/queue.tswithdrivers.redis({ connectionName: 'main' }). Dispatch any job → keys land in db 0. Confirmed by module resolution:Suggested fix
Duck-type instead of
instanceof— it can't false-negative across package copies:Failing that, please make the fallback throw on an unrecognised object rather than silently substituting defaults — the silence is what made this expensive to find.
Versions
Reproduced on both
@boringnode/queue@0.5.2(via@adonisjs/queue@0.6.1) and@boringnode/queue@0.6.0(via@adonisjs/queue@0.6.2) — same result, jobs land in db 0 either way.@adonisjs/redis@10.0.0, pnpm 11.15, Node 24. The check is unchanged onmain.