{"record":{"id":"869a18eb23e7e619","repo":"immich-app/immich","slug":"failed-to-decode-redis-options","errorCode":null,"errorMessage":"Failed to decode redis options","messagePattern":"Failed to decode redis options","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"server/src/repositories/config.repository.ts","lineNumber":216,"sourceCode":"    geodata: join(buildFolder, 'geodata'),\n    web: join(buildFolder, 'www'),\n  };\n\n  let redisConfig = {\n    host: dto.REDIS_HOSTNAME || 'redis',\n    port: dto.REDIS_PORT || 6379,\n    db: dto.REDIS_DBINDEX || 0,\n    username: dto.REDIS_USERNAME || undefined,\n    password: dto.REDIS_PASSWORD || undefined,\n    path: dto.REDIS_SOCKET || undefined,\n  };\n\n  const redisUrl = dto.REDIS_URL;\n  if (redisUrl && redisUrl.startsWith('ioredis://')) {\n    try {\n      redisConfig = JSON.parse(Buffer.from(redisUrl.slice(10), 'base64').toString());\n    } catch (error) {\n      throw new Error('Failed to decode redis options', { cause: error });\n    }\n  }\n\n  const includedTelemetries =\n    dto.IMMICH_TELEMETRY_INCLUDE === 'all'\n      ? new Set(Object.values(ImmichTelemetry))\n      : asSet<ImmichTelemetry>(dto.IMMICH_TELEMETRY_INCLUDE, []);\n\n  const excludedTelemetries = asSet<ImmichTelemetry>(dto.IMMICH_TELEMETRY_EXCLUDE, []);\n  const telemetries = setDifference(includedTelemetries, excludedTelemetries);\n  for (const telemetry of telemetries) {\n    if (!TELEMETRY_TYPES.has(telemetry)) {\n      throw new Error(`Invalid telemetry found: ${telemetry}`);\n    }\n  }\n\n  const databaseConnection: DatabaseConnectionParams = dto.DB_URL\n    ? { connectionType: 'url', url: dto.DB_URL }","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/repositories/config.repository.ts#L198-L234","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the cause to see whether it was a base64 decode error or a JSON syntax error.","Regenerate the payload with Buffer.from(JSON.stringify(opts)).toString('base64') and prefix with ioredis://.","Prefer a plain redis://[:password@]host:port[/db] URL unless you need advanced ioredis options."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const url = process.env.REDIS_URL ?? '';\nif (url.startsWith('ioredis://')) {\n  const json = Buffer.from(url.slice('ioredis://'.length), 'base64').toString('utf8');\n  JSON.parse(json); // throws early with a clear cause if malformed\n}","typeGuard":"const isIoredisScheme = (u: string): boolean => u.startsWith('ioredis://');","tryCatchPattern":"try {\n  startServer();\n} catch (e) {\n  if ((e as Error).message === 'Failed to decode redis options') {\n    console.error((e as Error).cause);\n    // switch to redis:// URL or fix base64 payload\n  } else throw e;\n}","preventionTips":["Prefer a plain redis:// URL unless advanced ioredis options are required.","Generate base64 payloads programmatically, not by hand.","Unit-test the encode/decode round-trip in your deploy tooling."],"tags":["configuration","redis","environment","startup","encoding"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}