immich-app/immich · critical · Error

Failed to decode redis options

Error message

Failed to decode redis options

What it means

When REDIS_URL starts with ioredis://, Immich treats the remainder as base64-encoded JSON ioredis options and tries to JSON.parse(Buffer.from(...).toString()). Any decode/parse failure is rethrown as Error('Failed to decode redis options', { cause }). Standard redis:// URLs are not affected — only the ioredis:// base64 scheme.

Source

Thrown at server/src/repositories/config.repository.ts:216

    geodata: join(buildFolder, 'geodata'),
    web: join(buildFolder, 'www'),
  };

  let redisConfig = {
    host: dto.REDIS_HOSTNAME || 'redis',
    port: dto.REDIS_PORT || 6379,
    db: dto.REDIS_DBINDEX || 0,
    username: dto.REDIS_USERNAME || undefined,
    password: dto.REDIS_PASSWORD || undefined,
    path: dto.REDIS_SOCKET || undefined,
  };

  const redisUrl = dto.REDIS_URL;
  if (redisUrl && redisUrl.startsWith('ioredis://')) {
    try {
      redisConfig = JSON.parse(Buffer.from(redisUrl.slice(10), 'base64').toString());
    } catch (error) {
      throw new Error('Failed to decode redis options', { cause: error });
    }
  }

  const includedTelemetries =
    dto.IMMICH_TELEMETRY_INCLUDE === 'all'
      ? new Set(Object.values(ImmichTelemetry))
      : asSet<ImmichTelemetry>(dto.IMMICH_TELEMETRY_INCLUDE, []);

  const excludedTelemetries = asSet<ImmichTelemetry>(dto.IMMICH_TELEMETRY_EXCLUDE, []);
  const telemetries = setDifference(includedTelemetries, excludedTelemetries);
  for (const telemetry of telemetries) {
    if (!TELEMETRY_TYPES.has(telemetry)) {
      throw new Error(`Invalid telemetry found: ${telemetry}`);
    }
  }

  const databaseConnection: DatabaseConnectionParams = dto.DB_URL
    ? { connectionType: 'url', url: dto.DB_URL }

View on GitHub (pinned to 199723261c)

Solutions

  1. Inspect the cause to see whether it was a base64 decode error or a JSON syntax error.
  2. Regenerate the payload with Buffer.from(JSON.stringify(opts)).toString('base64') and prefix with ioredis://.
  3. Prefer a plain redis://[:password@]host:port[/db] URL unless you need advanced ioredis options.
Defensive patterns

Strategy: validation

Validate before calling

const url = process.env.REDIS_URL ?? '';
if (url.startsWith('ioredis://')) {
  const json = Buffer.from(url.slice('ioredis://'.length), 'base64').toString('utf8');
  JSON.parse(json); // throws early with a clear cause if malformed
}

Type guard

const isIoredisScheme = (u: string): boolean => u.startsWith('ioredis://');

Try / catch

try {
  startServer();
} catch (e) {
  if ((e as Error).message === 'Failed to decode redis options') {
    console.error((e as Error).cause);
    // switch to redis:// URL or fix base64 payload
  } else throw e;
}

Prevention

When it happens

Trigger: Setting REDIS_URL=ioredis://<payload> where <payload> is not valid base64, or decodes to invalid JSON, or does not match the expected ioredis options shape.

Common situations: Generating the base64 payload incorrectly; trailing newline/whitespace in the encoded string; confusing this scheme with a normal redis://host:port URL; payload was encoded from a different ioredis version option keys.

Understand the failure class

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/869a18eb23e7e619. Report an issue: GitHub.