immich-app/immich · error · Error

Not in maintenance mode

Error message

Not in maintenance mode

What it means

MaintenanceService.createLoginUrl builds a one-time admin login URL for maintenance mode. When called without an explicit secret, it reads the current maintenance-mode state and uses state.secret. If maintenance mode is not currently active, there is no secret to use, so it throws a plain Error 'Not in maintenance mode'. This guards against generating URLs that could never authenticate.

Source

Thrown at server/src/services/maintenance.service.ts:85

    );
  }

  @OnEvent({ name: 'AppRestart', server: true })
  onRestart(event: ArgOf<'AppRestart'>, ack?: (ok: 'ok') => void): void {
    this.logger.log(`Restarting due to event... ${JSON.stringify(event)}`);

    ack?.('ok');
    this.appRepository.exitApp();
  }

  async createLoginUrl(auth: MaintenanceAuthDto, secret?: string): Promise<string> {
    const { server } = await this.getConfig({ withCache: true });
    const baseUrl = getExternalDomain(server);

    if (!secret) {
      const state = await this.getMaintenanceMode();
      if (!state.isMaintenanceMode) {
        throw new Error('Not in maintenance mode');
      }

      secret = state.secret;
    }

    return await createMaintenanceLoginUrl(baseUrl, auth, secret);
  }
}

View on GitHub (pinned to 199723261c)

Solutions

  1. Enable maintenance mode first (set isMaintenanceMode true), then call createLoginUrl without a secret.
  2. Or supply the secret explicitly to createLoginUrl if you hold one from when maintenance mode was enabled.
  3. Have callers check getMaintenanceMode() before requesting the URL.
  4. If maintenance ended, use the normal admin login flow instead.

Example fix

// before
await maintenanceApi.createLoginUrl(auth); // not in maintenance mode
// after
await maintenanceApi.setMaintenanceMode(true);
await maintenanceApi.createLoginUrl(auth);
Defensive patterns

Strategy: validation

Validate before calling

const state = await api.maintenanceApi.getMaintenanceMode();
if (!state.isMaintenanceMode) {
  throw new Error('Enable maintenance mode before requesting a maintenance login URL');
}
await api.maintenanceApi.createLoginUrl(auth);

Type guard

const isInMaintenanceMode = (s: { isMaintenanceMode: boolean }) =>
  s.isMaintenanceMode === true;

Try / catch

try {
  return await api.maintenanceApi.createLoginUrl(auth);
} catch (e) {
  if (/Not in maintenance mode/.test(String(e?.message))) {
    // either enable maintenance mode or pass an explicit secret
  } else throw e;
}

Prevention

When it happens

Trigger: Calling createLoginUrl(auth, undefined-secret) while getMaintenanceMode().isMaintenanceMode is false. E.g. an admin flow that generates the URL after maintenance mode was already turned off, or a scripted call outside a maintenance window.

Common situations: Race between turning off maintenance mode and a lingering UI action; calling the maintenance login endpoint outside maintenance; automation that does not check mode first.

Related errors


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