[ioredis] Unhandled error event: Error: connect ECONNREFUSED 127.0.0.1:6379

Node.js · updated 2026-11-04

Logged by ioredis (and node-redis) when a connection attempt is refused. ioredis keeps retrying and logs it again on each attempt; without an 'error' listener on the client it appears as an unhandled error event. 127.0.0.1:6379 is the client default, which is a strong hint the configured URL was never used.

[ioredis] Unhandled error event: Error: connect ECONNREFUSED 127.0.0.1:6379
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1606:16) {
  errno: -111,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 6379
}

Quick fix

If the address is 127.0.0.1:6379 in production, the client did not get your REDIS_URL: pass it explicitly (new Redis(process.env.REDIS_URL)) and make sure the variable is set. Add an 'error' listener so retries are logged, not unhandled, and decide whether the app can run without Redis.

  1. Look at the address: the client default (127.0.0.1:6379) means your configured URL was not used.
  2. Pass the URL explicitly to the client constructor and check the variable is set in that environment.
  3. Add client.on('error', ...) so connection errors are logged once and do not show as unhandled events.
  4. Inside Docker, use the Redis service name (redis:6379), not localhost.
  5. For hosted Redis with TLS, use a rediss:// URL.
typescript
import Redis from 'ioredis';

const url = process.env.REDIS_URL;
if (!url) throw new Error('REDIS_URL is not set');

export const redis = new Redis(url, {
  maxRetriesPerRequest: 3,          // fail commands instead of waiting forever
});
redis.on('error', (err) => logger.warn('redis error', { code: (err as any).code, message: err.message }));

Why it happens

REDIS_URL not set or not passed

new Redis() with no arguments connects to 127.0.0.1:6379. If the variable is missing, or the code builds the client before reading it, production tries localhost.

Redis not running

Locally, the Redis server was never started; in production, the managed instance was stopped, resized or moved.

Docker networking

Inside a container, localhost is the container itself; the Redis container is reachable by its service name.

TLS required by the host

Managed Redis services that require TLS refuse or drop plain connections; the URL needs the rediss:// scheme.

Other fixes

  • If Redis is only a cache, catch command errors and fall back to the source of truth instead of failing the request.
  • Set lazyConnect if the client should not connect at import time (for example in build steps).

Same error, other wording

  • Error: connect ECONNREFUSED 127.0.0.1:6379 (node-redis)
  • MaxRetriesPerRequestError: Reached the max retries per request limit (which is 20).
  • Error: getaddrinfo ENOTFOUND redis

Let your coding agent catch the next one

Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:

> Find the Redis ECONNREFUSED errors in production, show me which host and port the client used and where the client is created, and make sure it reads REDIS_URL and handles the error event.

Plans from $29/moSee what your agent readsSee the setupCancel any time from the billing portal

Related errors

More in Databases

References: ioredis on GitHub (connection options and events), Redis docs: connect with Node.js