immich-app/immich · error · BadRequestException
{error.message}
Error message
{error.message} What it means
Re-thrown (as BadRequestException) by SystemConfigService.updateSystemConfig from the catch around the ConfigValidate event emit. The error.message of whatever a ConfigValidate listener threw (e.g. the IMMICH_LOG_LEVEL guard, machine-learning settings, library watchers) is surfaced verbatim, after a logger.warn of the same text.
Source
Thrown at server/src/services/system-config.service.ts:71
const { logLevel } = this.configRepository.getEnv();
if (logLevel && !_.isEqual(toPlainObject(newConfig.logging), oldConfig.logging)) {
throw new Error('Logging cannot be changed while the environment variable IMMICH_LOG_LEVEL is set.');
}
}
async updateSystemConfig(dto: SystemConfigDto): Promise<SystemConfigDto> {
const { configFile } = this.configRepository.getEnv();
if (configFile) {
throw new BadRequestException('Cannot update configuration while IMMICH_CONFIG_FILE is in use');
}
const oldConfig = await this.getConfig({ withCache: false });
try {
await this.eventRepository.emit('ConfigValidate', { newConfig: toPlainObject(dto), oldConfig });
} catch (error) {
this.logger.warn(`Unable to save system config due to a validation error: ${error}`);
throw new BadRequestException(error instanceof Error ? error.message : error);
}
const newConfig = await this.updateConfig(dto);
await this.eventRepository.emit('ConfigUpdate', { newConfig, oldConfig });
return mapConfig(newConfig);
}
async getCustomCss(): Promise<string> {
const { theme } = await this.getConfig({ withCache: false });
return theme.customCss;
}
}
View on GitHub (pinned to 199723261c)
Solutions
- Read the returned message to identify which validator rejected the change (logging, library, ML, etc.).
- Address the specific validator's constraint (see related errors 189/190 or library/template docs).
- Re-open the config diff and fix the offending field, then retry the PUT.
- Inspect server logs for the matching 'Unable to save system config due to a validation error' line for full context.
Defensive patterns
Strategy: try-catch
Try / catch
try { await configApi.update(newConfig); }
catch (e) {
if (e instanceof BadRequestException) {
// e.message is the underlying validator's message;
// route it to the matching settings panel (logging, library, ML, etc.)
surfaceFieldError(e.message);
}
} Prevention
- Treat any PUT /system-config 400 as a domain-specific validation error, not a generic failure.
- Log the exact error.message for support tickets; it identifies the rejecting subsystem.
- Validate library/storage-template/ML fields locally before submit.
When it happens
Trigger: PUT /system-config where one or more ConfigValidate subscribers reject the new config (logging/env conflict, invalid OCR/machine-learning URL, duplicate storage templates, etc.).
Common situations: Admin UI save fails because some subsystem rejected the new config; the exact reason is listener-specific and carried in error.message.
Related errors
- Cannot update configuration while IMMICH_CONFIG_FILE is in u
- assetIds, albumId, or userId is required
- Real-time transcoding is not enabled
- Invalid job name
- Library ${id} not found
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/ced27bccb22e0d1b.
Report an issue: GitHub.