{"record":{"id":"46c64532789cc2cb","repo":"yikart/AiToEarn","slug":"responsecode-configeditorrestartfailed","errorCode":"ResponseCode.ConfigEditorRestartFailed","errorMessage":"ConfigEditorRestartFailed: error.message","messagePattern":"ConfigEditorRestartFailed: error\\.message","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/libs/config-editor/src/config-restart.service.ts","lineNumber":23,"sourceCode":"@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":5,"sourceCodeEnd":30,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/libs/config-editor/src/config-restart.service.ts#L5-L30","documentation":"restart() spawns `pm2 restart <pm_id>` via execFile; if spawning/execution throws synchronously (pm2 binary not on PATH, fork failure), the error is wrapped as AppException(ConfigEditorRestartFailed) with error.message. Note the callback-based execFile error (e.g. non-zero exit) is only logged, not thrown — this exception fires from the synchronous spawn path, typically 'spawn pm2 ENOENT'.","triggerScenarios":"pm_id exists but the `pm2` executable is not installed or not in the process's PATH (global npm bin missing, minimal container image, nvm PATH not loaded in the service's environment), or execFile throws synchronously for another spawn reason.","commonSituations":"App runs under pm2 via pm2-runtime but the pm2 CLI binary is absent in the runtime image; PATH differs between the shell that started pm2 and the service user; global npm prefix not in PATH under systemd/docker; permission to execute the binary missing.","solutions":["Read error.message — 'spawn pm2 ENOENT' means install pm2 or add its global bin dir to PATH in the service environment","Install pm2 in the image/runtime (`npm i -g pm2`) or reference the local node_modules/.bin/pm2","Start pm2 and the app from the same environment so PATH matches; check with `which pm2` as the service user","Consider replacing execFile('pm2') with process.exit() plus a pm2/systemd supervisor restart policy to avoid PATH dependence"],"exampleFix":"// before (Dockerfile)\nCMD [\"pm2-runtime\", \"app.js\"]\nRUN rm -rf /usr/local/lib/node_modules/pm2\n// after\nRUN npm i -g pm2 && pm2 install pm2-logrotate\nCMD [\"pm2-runtime\", \"app.js\"]","handlingStrategy":"try-catch","validationCode":"import { execFileSync } from 'node:child_process'\ntry { execFileSync('which', ['pm2']) } catch { throw new Error('pm2 binary not on PATH') }","typeGuard":null,"tryCatchPattern":"try {\n  restartService.restart()\n} catch (e) {\n  if (e instanceof AppException && e.code === ResponseCode.ConfigEditorRestartFailed && e.message.includes('ENOENT'))\n    logger.error('pm2 binary missing from PATH in service environment')\n  throw e\n}","preventionTips":["Install pm2 globally (or vendor it) in runtime images","Start pm2 and the app from the same shell/PATH environment","Check `which pm2` as the exact service user, not just interactively","Prefer supervisor-based restart policies over shelling out to pm2 where possible"],"tags":["pm2","process-management","spawn","path"],"backgroundTag":"spawn-enoent","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}