{"record":{"id":"eb86440f4093b824","repo":"yikart/AiToEarn","slug":"responsecode-configeditorpm2unavailable","errorCode":"ResponseCode.ConfigEditorPm2Unavailable","errorMessage":"ConfigEditorPm2Unavailable","messagePattern":"ConfigEditorPm2Unavailable","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/libs/config-editor/src/config-restart.service.ts","lineNumber":12,"sourceCode":"import { execFile } from 'node:child_process'\nimport { Injectable, Logger } from '@nestjs/common'\nimport { AppException, ResponseCode } from '@yikart/common'\n\n@Injectable()\nexport class ConfigRestartService {\n  private readonly logger = new Logger(ConfigRestartService.name)\n\n  restart(): void {\n    const pm2ProcessId = process.env['pm_id']\n    if (!pm2ProcessId) {\n      throw new AppException(ResponseCode.ConfigEditorPm2Unavailable)\n    }\n\n    try {\n      const child = execFile('pm2', ['restart', pm2ProcessId], (error) => {\n        if (error)\n          this.logger.error(error)\n      })\n      child.unref()\n    }\n    catch (error) {\n      throw new AppException(\n        ResponseCode.ConfigEditorRestartFailed,\n        error instanceof Error ? error.message : String(error),\n      )\n    }\n  }\n}\n","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/libs/config-editor/src/config-restart.service.ts#L1-L30","documentation":"ConfigRestartService.restart() can only restart the process if it is managed by PM2: it reads process.env['pm_id'], the per-process id PM2 injects. When pm_id is absent the process is not running under PM2 (or the env var was stripped), so restart() throws ConfigEditorPm2Unavailable instead of attempting `pm2 restart`.","triggerScenarios":"restart() is called (e.g. after saving a config that requires a process restart) while the Node process was started directly (node dist/main.js, nx serve, nodemon, docker CMD without pm2) or inside a container where PM2's env vars are not propagated.","commonSituations":"Local dev with `pnpm server:serve` where config changes can't hot-restart; Docker containers running the app without pm2-runtime; pm2 started via a wrapper that drops env vars (pm2 with --no-daemon in odd shells, or `pm2 start node --` losing env); a migration from pm2 to systemd/kubernetes where the restart endpoint still exists.","solutions":["Run the service under PM2 (`pm2 start ...`) so pm_id is set in the environment","If in Docker, use `pm2-runtime start ecosystem.config.js` as the container entrypoint, or drop the restart feature in favor of container restarts","Verify the env var survives: `console.log(process.env.pm_id)` at startup; if empty, fix how pm2 launches the app","Remove/disable the restart endpoint if the deployment target is systemd/Kubernetes where pm2 restart is meaningless"],"exampleFix":"// before (Dockerfile CMD)\nCMD [\"node\", \"dist/main.js\"]\n// after\nCMD [\"pm2-runtime\", \"ecosystem.config.js\"]","handlingStrategy":"fallback","validationCode":"if (!process.env.pm_id)\n  throw new Error('Not running under PM2: restart endpoint unavailable')","typeGuard":"function isPm2Managed(env = process.env): boolean {\n  return typeof env.pm_id === 'string' && env.pm_id !== ''\n}","tryCatchPattern":"try {\n  restartService.restart()\n} catch (e) {\n  if (e instanceof AppException && e.code === ResponseCode.ConfigEditorPm2Unavailable)\n    logger.warn('No pm_id: falling back to external restart (docker restart / manual)')\n}","preventionTips":["Run production services via pm2 or pm2-runtime so pm_id exists","In containers, prefer container-native restarts and hide the pm2 restart endpoint","Verify pm_id at startup with a health check","Do not strip PM2_* env vars with custom env sanitization"],"tags":["pm2","process-management","environment","deployment"],"backgroundTag":"pm2-not-available","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}