{"record":{"id":"65346ab616d4eef3","repo":"immich-app/immich","slug":"not-in-maintenance-mode","errorCode":null,"errorMessage":"Not in maintenance mode","messagePattern":"Not in maintenance mode","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/controllers/maintenance.controller.ts","lineNumber":54,"sourceCode":"  @Endpoint({\n    summary: 'Detect existing install',\n    description: 'Collect integrity checks and other heuristics about local data.',\n    history: new HistoryBuilder().added('v2.5.0').alpha('v2.5.0'),\n  })\n  @Authenticated({ permission: Permission.Maintenance, admin: true })\n  detectPriorInstall(): Promise<MaintenanceDetectInstallResponseDto> {\n    return this.service.detectPriorInstall();\n  }\n\n  @Post('login')\n  @Endpoint({\n    summary: 'Log into maintenance mode',\n    description: 'Login with maintenance token or cookie to receive current information and perform further actions.',\n    history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),\n  })\n  @Authenticated({ public: true })\n  maintenanceLogin(@Body() _dto: MaintenanceLoginDto): MaintenanceAuthDto {\n    throw new BadRequestException('Not in maintenance mode');\n  }\n\n  @Post()\n  @Endpoint({\n    summary: 'Set maintenance mode',\n    description: 'Put Immich into or take it out of maintenance mode',\n    history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),\n  })\n  @Authenticated({ permission: Permission.Maintenance, admin: true })\n  async setMaintenanceMode(\n    @Auth() auth: AuthDto,\n    @Body() dto: SetMaintenanceModeDto,\n    @GetLoginDetails() loginDetails: LoginDetails,\n    @Res({ passthrough: true }) res: Response,\n  ): Promise<void> {\n    if (dto.action === MaintenanceAction.End) {\n      return;\n    }","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/controllers/maintenance.controller.ts#L36-L72","documentation":"This BadRequestException is thrown by the maintenanceLogin endpoint (POST /maintenance/login) whenever the server is not currently in maintenance mode. The endpoint is a stub that unconditionally rejects login attempts unless maintenance mode was already activated; its purpose is only to let an already-maintenancing server hand out maintenance auth. It is always a 400 response with the message 'Not in maintenance mode'.","triggerScenarios":"Calling POST /maintenance/login (MaintenanceLoginDto body) against an Immich server whose maintenance mode is not active (this.setStatus({active:false}) or never activated). Any request to this endpoint while the server runs normally returns this error.","commonSituations":"Clients or scripts attempting to log into the maintenance web UI after maintenance mode already ended (setAction cleared it); hitting the maintenance endpoint on a normal production install; a race where the server exited maintenance mode before the login request arrived; stale browser tab pointing at a server that rebooted out of maintenance.","solutions":["Check that maintenance mode is actually active before calling maintenanceLogin (call the status endpoint first).","If maintenance is done, stop attempting maintenance login and use the normal authentication flow instead.","Restart maintenance mode via the setAction endpoint (POST /maintenance) with the correct mode if you still need maintenance access.","Verify you are hitting the right server/port — a normal (non-maintenance) Immich instance will always return this error."],"exampleFix":"// before\nclass Client {\n  async maintenanceLogin(token: string) {\n    return this.post('/maintenance/login', { token }); // 400 Not in maintenance mode\n  }\n}\n// after\nclass Client {\n  async maintenanceLogin(token: string) {\n    const status = await this.get('/maintenance/status');\n    if (!status.active) {\n      throw new Error('Server is not in maintenance mode; skipping maintenance login');\n    }\n    return this.post('/maintenance/login', { token });\n  }\n}","handlingStrategy":"try-catch","validationCode":"const status = await fetch(`${base}/maintenance/status`).then(r => r.json());\nif (!status.active) {\n  throw new Error('Server not in maintenance mode; maintenance login unavailable');\n}","typeGuard":"function isMaintenanceActive(s: unknown): s is { active: true } {\n  return typeof s === 'object' && s !== null && (s as { active?: unknown }).active === true;\n}","tryCatchPattern":"try {\n  await api.post('/maintenance/login', dto);\n} catch (e) {\n  if (e instanceof BadRequestException && e.message === 'Not in maintenance mode') {\n    // fall back to normal login flow\n  } else throw e;\n}","preventionTips":["Query the maintenance status endpoint before attempting maintenance login.","Use the normal auth flow once maintenance mode has ended.","Point clients at the correct server instance (normal installs always reject this endpoint).","Handle stale maintenance sessions in the UI by refreshing status."],"tags":["http-400","maintenance-mode","bad-request","api"],"backgroundTag":"invalid-state-transition","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}