symfony/symfony · error · InvalidArgumentException

Invalid Redis DSN: it does not start with "redis[s]:" nor…

Error message

Invalid Redis DSN: it does not start with "redis[s]:" nor "valkey[s]:".

What it means

Thrown by RedisAdapter::createConnection() when the DSN string does not begin with one of the supported schemes: redis:, rediss:, valkey:, valkeys:. The method uses a match expression on str_starts_with for each scheme; the default arm throws. This is the first validation in DSN parsing, before any host or auth processing.

Solutions

  1. Prefix the DSN correctly: 'redis://localhost:6379', 'rediss://...' for TLS, 'valkey://...' for Valkey.
  2. For unix sockets use 'redis:///var/run/redis.sock'.
  3. Check the DSN comes from the intended environment variable and is not empty.
  4. If migrating from Predis/PhpRedis directly, translate their config into a Symfony redis:// DSN.

Example fix

// before
$redis = RedisAdapter::createConnection('tcp://127.0.0.1:6379');

// after
$redis = RedisAdapter::createConnection('redis://127.0.0.1:6379');
Defensive patterns

Strategy: validation

Validate before calling

if (!preg_match('#^(redis|rediss|valkey|valkeys):#', $dsn)) {
    throw new \InvalidArgumentException('Redis DSN must start with redis[s]: or valkey[s]:');
}
$redis = RedisAdapter::createConnection($dsn);

Type guard

function isValidRedisScheme(string $dsn): bool
{
    return (bool) preg_match('#^(redis|rediss|valkey|valkeys):#', $dsn);
}

Try / catch

try {
    $redis = RedisAdapter::createConnection($dsn);
} catch (\Symfony\Component\Cache\Exception\InvalidArgumentException $e) {
    if (str_contains($e->getMessage(), 'does not start with')) {
        // DSN scheme is wrong; surface to ops, do not auto-correct blindly
        throw $e;
    }
}

Prevention

When it happens

Trigger: Passing a DSN like 'tcp://localhost:6379', 'memcached://...', 'localhost:6379' (no scheme), or a typo like 'reddis://...'. Any string lacking a recognised redis/valkey scheme prefix triggers the error immediately.

Common situations: Using a generic connection URL from another library. Copying a DSN from a Memcached orDoctrine cache config. Typo in the scheme. Forgetting the scheme entirely. Env var not set so the DSN is empty or a placeholder.

Related errors


AI-assisted analysis of symfony/symfony@3b11ffbe25 (2026-08-11). Data as JSON: /api/errors/756cc3bff6171785. Report an issue: GitHub.

Appendix: source

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

     * Example DSN:
     *   - redis://localhost
     *   - redis://example.com:1234
     *   - redis://secret@example.com/13
     *   - redis:///var/run/redis.sock
     *   - redis://secret@/var/run/redis.sock/13
     *
     * @param array $options See self::$defaultConnectionOptions
     *
     * @throws InvalidArgumentException when the DSN is invalid
     */
    public static function createConnection(#[\SensitiveParameter] string $dsn, array $options = []): \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|Relay|RelayCluster
    {
        $scheme = match (true) {
            str_starts_with($dsn, 'redis:') => 'redis',
            str_starts_with($dsn, 'rediss:') => 'rediss',
            str_starts_with($dsn, 'valkey:') => 'valkey',
            str_starts_with($dsn, 'valkeys:') => 'valkeys',
            default => throw new InvalidArgumentException('Invalid Redis DSN: it does not start with "redis[s]:" nor "valkey[s]:".'),
        };

        if (!\extension_loaded('redis') && !\extension_loaded('relay') && !class_exists(\Predis\Client::class)) {
            throw new CacheException('Cannot find the "redis" extension nor the "relay" extension nor the "predis/predis" package.');
        }

        $auth = null;
        $params = preg_replace_callback('#^'.$scheme.':(//)?(?:(?:(?<user>[^:@]*+):)?(?<password>[^@]*+)@)?#', static function ($m) use (&$auth) {
            if (isset($m['password'])) {
                if (\in_array($m['user'], ['', 'default'], true)) {
                    $auth = rawurldecode($m['password']);
                } else {
                    $auth = [rawurldecode($m['user']), rawurldecode($m['password'])];
                }

                if ('' === $auth) {
                    $auth = null;
                }

View on GitHub (pinned to 3b11ffbe25)