immich-app/immich · critical

Invalid environment variables:

Error message

Invalid environment variables: 

What it means

getEnv validates process.env against a Zod schema (EnvSchema.safeParse). When any required or format-constrained environment variable fails validation, the server refuses to start and throws this error, appending one ' - [path] message' line per Zod issue so every offending variable is listed at once.

Solutions

  1. Read the issue lines under the message — each names the exact variable and Zod reason — and fix or set those variables in your .env / compose environment
  2. Use server/.env as a template and fill in all required values (DB_*, REDIS_*)
  3. If upgrading, check the release notes/migrations for newly required environment variables
  4. Test locally with the same env: run the server once with the exported env and read the first failing entry

Example fix

# before
DB_HOSTNAME=  # empty/missing
# after
DB_HOSTNAME=database
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_DATABASE_NAME=immich
Defensive patterns

Strategy: validation

Validate before calling

import { EnvSchema } from 'server/src/schema';
const result = EnvSchema.safeParse(process.env);
if (!result.success) {
  for (const i of result.error.issues) console.error(`  - [${i.path.join('.')}] ${i.message}`);
  process.exit(1);
}

Type guard

const envIsValid = (env: NodeJS.ProcessEnv): env is NodeJS.ProcessEnv & Record<string, string> => EnvSchema.safeParse(env).success;

Try / catch

try { const config = getEnv(); } catch (e) { if ((e as Error).message.startsWith('Invalid environment variables:')) { console.error('Fix the listed env vars in .env / compose:'); console.error((e as Error).message); process.exit(1); } throw e; }

Prevention

When it happens

Trigger: Calling getEnv() (at config/repository initialization) with process.env missing required variables (e.g. DB_HOSTNAME, DB_PASSWORD, REDIS_HOSTNAME in non-URL mode) or variables with values that fail Zod checks (bad URLs, non-numeric ports, invalid enums like IMMICH_ENV not in {production,development}).

Common situations: Fresh deployments with an incomplete .env file; typo'd variable names so the schema sees them as missing; upgrading immich where new required env vars were introduced; docker-compose files missing env_file wiring; setting IMMICH_WORKERS_INCLUDE to a value outside the enum.

Understand the failure class

Background: "is not a valid" / "Invalid ... value" environment variable errors: how libraries validate env vars and what to do when they reject yours — this error's family across 48 libraries.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/93e8f6c2e688dae8. Report an issue: GitHub.

Appendix: source

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

  helmetFile = helmetFile === 'true' ? join(import.meta.dirname, '..', '..', 'helmet.json') : helmetFile;

  try {
    return JSON.parse(readFileSync(helmetFile).toString()) as HelmetOptions;
  } catch (error) {
    throw new Error(`Failed to read helmet file: ${helmetFile}`, { cause: error });
  }
};

const getEnv = (): EnvData => {
  const parseResult = EnvSchema.safeParse(process.env);
  if (!parseResult.success) {
    const messages = ['Invalid environment variables: '];
    for (const issue of parseResult.error.issues) {
      const path = issue.path.join('.');
      messages.push(`  - [${path}] ${issue.message}`);
    }
    throw new Error(messages.join('\n'));
  }
  const dto = parseResult.data;

  const includedWorkers = asSet(dto.IMMICH_WORKERS_INCLUDE, [ImmichWorker.Api, ImmichWorker.Microservices]);
  const excludedWorkers = asSet(dto.IMMICH_WORKERS_EXCLUDE, []);
  const workers = [...setDifference(includedWorkers, excludedWorkers)];
  for (const worker of workers) {
    if (!WORKER_TYPES.has(worker)) {
      throw new Error(`Invalid worker(s) found: ${workers.join(',')}`);
    }
  }

  const environment = dto.IMMICH_ENV || ImmichEnvironment.Production;
  const isProd = environment === ImmichEnvironment.Production;
  const buildFolder = dto.IMMICH_BUILD_DATA || '/build';
  const folders = {
    geodata: join(buildFolder, 'geodata'),
    web: join(buildFolder, 'www'),

View on GitHub (pinned to e55ac299a4)