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
- 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
- Use server/.env as a template and fill in all required values (DB_*, REDIS_*)
- If upgrading, check the release notes/migrations for newly required environment variables
- 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
- Start from server/.env.example and fill every required key
- Keep .env referenced in docker-compose via env_file so vars actually reach the container
- After upgrading immich, re-run validation against the new EnvSchema before deploying
- Never leave required DB_*/REDIS_* values empty — comment them out only if you truly use DB_URL/REDIS_URL modes
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
- Invalid system config:
- Invalid environment variables: \n
- Invalid system config
- Invalid telemetry found
- Invalid worker(s) found
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)