{"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":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/repositories/config.repository.ts","lineNumber":212,"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":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/repositories/config.repository.ts#L194-L230","documentation":"REDIS_URL may carry ioredis options as a base64-encoded JSON blob prefixed with 'ioredis://'. When that suffix is present, getEnv decodes slice(10) from base64 and JSON.parses it; any decode/parse failure (invalid base64, or base64 that is not valid JSON) is wrapped in this error.","triggerScenarios":"REDIS_URL starts with 'ioredis://' but the remainder is not valid base64 (spaces, URL-encoding, truncated string) or decodes to bytes that are not valid JSON (e.g. plain 'redis://host' content pasted after the prefix).","commonSituations":"Users copying an example REDIS_URL and replacing the JSON payload but leaving it unencoded; YAML/compose interpolating quotes or newlines into the value; a sentinel/template string left unfinished; manually base64-encoding with padding stripped incorrectly or double-encoding.","solutions":["Regenerate the payload: node -e 'console.log(\"ioredis://\"+Buffer.from(JSON.stringify({host:\"redis\"})).toString(\"base64\"))' and use the output as REDIS_URL","Verify the suffix decodes: node -e 'JSON.parse(Buffer.from(process.argv[1].slice(10),\"base64\"))' \"$REDIS_URL\"","If you just want a plain connection, drop the ioredis:// scheme and set REDIS_URL=redis://redis:6379 (or REDIS_HOSTNAME etc.) instead","Check the compose/env layer isn't mangling the value (no stray quotes, spaces, or line breaks)"],"exampleFix":"// before\nREDIS_URL=ioredis://redis:6379\n// after\nREDIS_URL=redis://redis:6379\n# or properly encoded:\nREDIS_URL=ioredis://eyJob3N0IjoicmVkaXMifQ==","handlingStrategy":"validation","validationCode":"const url = process.env.REDIS_URL;\nif (url?.startsWith('ioredis://')) {\n  try {\n    JSON.parse(Buffer.from(url.slice(10), 'base64').toString());\n  } catch (e) {\n    throw new Error(`REDIS_URL is not valid base64-encoded JSON: ${e}`);\n  }\n}","typeGuard":"const isEncodedRedisOptions = (v: string | undefined): boolean =>\n  !!v?.startsWith('ioredis://') && (() => { try { JSON.parse(Buffer.from(v.slice(10), 'base64').toString()); return true; } catch { return false; } })();","tryCatchPattern":"try { start(); } catch (e) { if ((e as Error).message === 'Failed to decode redis options') { console.error('REDIS_URL must be redis://... or ioredis:// + base64(JSON). Re-encode:', e.cause); process.exit(1); } throw e; }","preventionTips":["Prefer plain redis:// URLs unless you need advanced ioredis options","Generate the encoded value with Buffer.from(JSON.stringify(opts)).toString('base64') — never hand-write it","Ensure the env layer (compose/K8s) does not add quotes, spaces, or line wraps to the value","Test-decode REDIS_URL in CI before deploying"],"tags":["redis","config","base64","json","env"],"backgroundTag":"invalid-env-var-value","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}