immich-app/immich · critical · Error
Failed to read helmet file: ${helmetFile}
Error message
Failed to read helmet file: ${helmetFile} What it means
getHelmetOptions() reads the helmet configuration file path (from env, defaulting to the packaged helmet.json when the value is 'true'). It attempts readFileSync then JSON.parse; any I/O or parse failure is rethrown as Error('Failed to read helmet file: <path>', { cause }). This runs during server config bootstrap, so a bad helmet file prevents startup.
Source
Thrown at server/src/repositories/config.repository.ts:169
return new Set(values.length === 0 ? defaults : (values as T[]));
};
const resolveHelmetFile = (helmetFile: 'true' | 'false' | string | undefined) => {
// default is off
if (!helmetFile || helmetFile === 'false') {
return;
}
helmetFile =
helmetFile === 'true'
? // eslint-disable-next-line unicorn/prefer-module
join(__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)];View on GitHub (pinned to 199723261c)
Solutions
- Check the cause on the thrown error to distinguish ENOENT (missing) from SyntaxError (bad JSON) from EACCES (permissions).
- Correct the path / fix the JSON / fix file permissions, then restart.
- If you do not need a custom helmet config, unset the env var so the default packaged file is used.
Defensive patterns
Strategy: validation
Validate before calling
import { existsSync, readFileSync } from 'node:fs';
if (helmetFile && helmetFile !== 'true' && !existsSync(helmetFile)) {
throw new Error(`Helmet file not found: ${helmetFile}`);
}
if (helmetFile) JSON.parse(readFileSync(helmetFile, 'utf8')); // pre-validate JSON Try / catch
try {
startServer();
} catch (e) {
if ((e as Error).message.startsWith('Failed to read helmet file')) {
console.error((e as Error).cause); // ENOENT vs SyntaxError
// fix path/JSON/perms then restart
} else throw e;
} Prevention
- Validate the helmet file path and JSON in your deploy script before boot.
- Use the default packaged helmet.json unless you specifically need a custom one.
- Lint any custom JSON before deploying.
When it happens
Trigger: Setting the helmet env var to a path that does not exist, is unreadable, or contains invalid JSON; or the default packaged helmet.json is missing/corrupt in the deployment.
Common situations: Custom deployment that mounts a helmet config with a typo'd path; permission error reading the file; hand-edited JSON with a trailing comma; broken container image missing the bundled file.
Related errors
- Invalid environment variables: \n - [${path}] ${issue.messa
- Invalid worker(s) found: ${workers.join(',')}
- Failed to decode redis options
- Invalid telemetry found: ${telemetry}
- Unable to determine worker type
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/ec514a21c1b0af2a.
Report an issue: GitHub.