immich-app/immich · warning · BadRequestException
Not in maintenance mode
Error message
Not in maintenance mode
What it means
The POST /maintenance/login controller method maintenanceLogin unconditionally throws BadRequestException('Not in maintenance mode'). The real login logic lives in the maintenance worker service and only runs when the server boots in maintenance mode; this controller stub exists so the OpenAPI route is always documented. Hitting it on a normally-running server is expected to fail.
Source
Thrown at server/src/controllers/maintenance.controller.ts:54
@Endpoint({
summary: 'Detect existing install',
description: 'Collect integrity checks and other heuristics about local data.',
history: new HistoryBuilder().added('v2.5.0').alpha('v2.5.0'),
})
@Authenticated({ permission: Permission.Maintenance, admin: true })
detectPriorInstall(): Promise<MaintenanceDetectInstallResponseDto> {
return this.service.detectPriorInstall();
}
@Post('login')
@Endpoint({
summary: 'Log into maintenance mode',
description: 'Login with maintenance token or cookie to receive current information and perform further actions.',
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
})
@Authenticated({ public: true })
maintenanceLogin(@Body() _dto: MaintenanceLoginDto): MaintenanceAuthDto {
throw new BadRequestException('Not in maintenance mode');
}
@Post()
@Endpoint({
summary: 'Set maintenance mode',
description: 'Put Immich into or take it out of maintenance mode',
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
})
@Authenticated({ permission: Permission.Maintenance, admin: true })
async setMaintenanceMode(
@Auth() auth: AuthDto,
@Body() dto: SetMaintenanceModeDto,
@GetLoginDetails() loginDetails: LoginDetails,
@Res({ passthrough: true }) res: Response,
): Promise<void> {
if (dto.action === MaintenanceAction.End) {
return;
}View on GitHub (pinned to 199723261c)
Solutions
- Restart Immich in maintenance mode (set the maintenance worker / boot flag as documented for your install) before calling login.
- If you did not intend to be in maintenance, do not call this endpoint; use normal auth flows instead.
- Check server status via the maintenance status endpoint to confirm the current mode before attempting login.
Defensive patterns
Strategy: validation
Validate before calling
// call status endpoint before login to confirm mode
const status = await fetch(`${baseUrl}/maintenance/status`).then(r => r.json());
if (!status.maintenanceEnabled) {
throw new Error('Server is not in maintenance mode; restart it in maintenance first.');
} Try / catch
try {
await fetch(`${baseUrl}/maintenance/login`, { method: 'POST', body });
} catch (e) {
if ((e as Error).message.includes('Not in maintenance mode')) {
// restart server in maintenance, or switch to normal auth
} else throw e;
} Prevention
- Confirm maintenance mode via the status endpoint before calling login.
- Only call /maintenance/login when an operator has intentionally booted maintenance.
- Keep automation aware of the maintenance flag via status checks, not blind calls.
When it happens
Trigger: Sending POST /maintenance/login (with a maintenance token/cookie body) against an Immich server that is NOT started in maintenance mode.
Common situations: Operator forgets to start the container/process with the maintenance flag; the endpoint is hit by automation/probes during normal operation; confusion about whether the server is currently in maintenance.
Related errors
- Missing JWT Token
- Invalid JWT Token
- Unable to determine worker type
- Failed to add job handler for ${label}
- Failed to find job handler for Job.${jobKey} ("${jobName}")
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/65346ab616d4eef3.
Report an issue: GitHub.