symfony/symfony · error · Symfony\Component\Cache\Exception\InvalidArgumentException

Redis connection failed: {message}

Error message

Redis connection failed: {message}

What it means

Thrown when the phpredis \Redis (or Relay) client raises a \RedisException / \Relay::Exception during the actual connect/open attempt in Symfony's Redis adapter initializer (RedisTrait.php:344-346). Symfony wraps the raw driver exception so the underlying message (connection refused, auth failed, DNS error, timeout) is preserved while normalizing it as an InvalidArgumentException. For non-lazy DSNs it fires at createConnection() time; for lazy DSNs it is deferred to the first cache operation.

Source

Thrown at src/Symfony/Component/Cache/Traits/RedisTrait.php:345

                        $isConnected = $redis->isConnected();
                    } finally {
                        restore_error_handler();
                    }
                    if (!$isConnected) {
                        $error = preg_match('/^Redis::p?connect\(\): (.*)/', $error ?? $redis->getLastError() ?? '', $error) ? \sprintf(' (%s)', $error[1]) : '';
                        throw new InvalidArgumentException('Redis connection failed: '.$error.'.');
                    }

                    if (0 < $params['tcp_keepalive'] && (!$isRedisExt || \defined('Redis::OPT_TCP_KEEPALIVE'))) {
                        $redis->setOption($isRedisExt ? \Redis::OPT_TCP_KEEPALIVE : Relay::OPT_TCP_KEEPALIVE, $params['tcp_keepalive']);
                    }

                    if (!$redis->select($params['dbindex'])) {
                        $e = preg_replace('/^ERR /', '', $redis->getLastError());
                        throw new InvalidArgumentException('Redis connection failed: '.$e.'.');
                    }
                } catch (\RedisException|\Relay\Exception $e) {
                    throw new InvalidArgumentException('Redis connection failed: '.$e->getMessage());
                }

                return $redis;
            };

            if ($params['lazy']) {
                $redis = $isRedisExt ? RedisProxy::createLazyProxy($initializer) : RelayProxy::createLazyProxy($initializer);
            } else {
                $redis = $initializer();
            }
        } elseif (is_a($class, \RedisArray::class, true)) {
            foreach ($hosts as $i => $host) {
                $hosts[$i] = match ($host['scheme']) {
                    'tcp' => $host['host'].':'.$host['port'],
                    'tls' => 'tls://'.$host['host'].':'.$host['port'],
                    default => $host['path'],
                };
            }

View on GitHub (pinned to 698e28026c)

Solutions

  1. Verify reachability: run 'redis-cli -h <host> -p <port> -a <pass> ping' (or with --user for ACL) and confirm you get PONG.
  2. Correct the DSN scheme/auth: 'redis://user:password@host:port/db' (ACL) or 'redis://:password@host' (legacy requirepass); use 'rediss://' only for TLS endpoints.
  3. Ensure the Redis service is up and the port is open between the app host and Redis (security groups, Docker network, k8s Service).
  4. For lazy pools, set 'lazy: false' during a deployment smoke test to surface connect errors at boot instead of on first request.
  5. Raise timeout/read_timeout in the DSN query string only after confirming connectivity (e.g. '?timeout=2&read_timeout=2').

Example fix

// before
$dsn = 'redis://localhost:6379'; // fails: server on another host / needs auth
$pool = RedisAdapter::createConnection($dsn);

// after
$dsn = 'redis://appuser:s3cret@redis.internal:6379/0';
// validate first
$pool = RedisAdapter::createConnection($dsn, ['lazy' => false]);
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight reachability before handing the DSN to the adapter
function redisDsnOk(string $dsn, float $timeout = 1): bool {
    $u = parse_url($dsn);
    if (!isset($u['host'], $u['port'])) return false;
    $errno = 0; $errstr = '';
    $fp = @fsockopen($u['host'], $u['port'], $errno, $errstr, $timeout);
    if (!is_resource($fp)) return false;
    fclose($fp);
    return true;
}
if (!redisDsnOk($dsn)) { /* fall back / alert */ }

Try / catch

// wrap the connection build; retry transient network errors with backoff
use Symfony\Component\Cache\Exception\InvalidArgumentException;
$attempt = 0;
do {
    try {
        $conn = RedisAdapter::createConnection($dsn, ['lazy' => false]);
        break;
    } catch (InvalidArgumentException $e) {
        if (++$attempt >= 3 || !str_contains($e->getMessage(), 'connection')) { throw $e; }
        usleep(200_000 * $attempt);
    }
} while (true);

Prevention

When it happens

Trigger: Calling RedisAdapter::createConnection('redis://host:6379') or building a Redis-backed cache/session/lock pool when $redis->{connect|pconnect}(...) throws. Concretely: server unreachable, AUTH/requirepass mismatch, invalid ACL user, wrong dbindex select, TLS handshake failure, or read_timeout exceeded during open.

Common situations: Redis not running in local dev; wrong host/port in cache DSN; password set on server but missing/wrong in DSN; firewall/security group blocking 6379; connecting with 'rediss://' (TLS) to a non-TLS port; Redis 6 ACLs requiring a username but DSN uses 'redis://:pass@host'.

Related errors


AI-assisted analysis of symfony/symfony@698e28026c (2026-08-06). Data as JSON: /api/errors/a6de6cbc034ef33c. Report an issue: GitHub.