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

  1. Restart Immich in maintenance mode (set the maintenance worker / boot flag as documented for your install) before calling login.
  2. If you did not intend to be in maintenance, do not call this endpoint; use normal auth flows instead.
  3. 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

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


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/65346ab616d4eef3. Report an issue: GitHub.