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

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

Error message

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

What it means

createConnection() begins by detecting the DSN scheme with a match expression. Only redis:, rediss:, valkey:, valkeys: are recognised; anything else is rejected immediately as a clearly invalid Redis DSN rather than producing a confusing parse failure later.

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 698e28026c)

Solutions

  1. Prefix the DSN with a supported scheme: redis://, rediss:// (TLS), valkey://, valkeys://.
  2. Validate the env value at boot: if (!preg_match('#^redis[s]?://|^valkey[s]?://#', $dsn)) fail loud.
  3. Check for accidental whitespace: trim($dsn).

Example fix

// before
RedisAdapter::createConnection(getenv('REDIS_URL')); // 'localhost:6379'

// after
RedisAdapter::createConnection('redis://'.trim(getenv('REDIS_URL')));
Defensive patterns

Strategy: validation

Validate before calling

$dsn = trim((string) $dsn);
if (!preg_match('#^redis[s]?://|^valkey[s]?://#', $dsn)) {
    throw new \InvalidArgumentException('REDIS_URL must start with redis[s]:/valkey[s]:');
}

Type guard

function validRedisScheme(string $dsn): bool {
    return (bool) preg_match('#^redis[s]?://|^valkey[s]?://#', $dsn);
}

Prevention

When it happens

Trigger: Passing 'memcached://localhost'; a typo like 'reddis://' or 'rediss' (no colon); an env var that resolved to an empty string; passing a host without a scheme ('localhost:6379').

Common situations: Wrong REDIS_URL scheme; copy-pasted a Doctrine cache URL expecting memcached; trailing-space or BOM in env var stripping the scheme.

Related errors


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