{"record":{"id":"f5e9374eecee768b","repo":"paperclipai/paperclip","slug":"status-only-recovery-runs-cannot-create-or-modify","errorCode":null,"errorMessage":"Status-only recovery runs cannot create or modify approvals","messagePattern":"Status-only recovery runs cannot create or modify approvals","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"server/src/routes/approvals.ts","lineNumber":193,"sourceCode":"  async function assertApprovalMutationAllowedByRunContext(req: Request, res: any, companyId: string) {\n    if (req.actor.type !== \"agent\") return true;\n    const runId = req.actor.runId?.trim();\n    if (!runId || !req.actor.agentId) return true;\n\n    const run = await db\n      .select({\n        id: heartbeatRuns.id,\n        companyId: heartbeatRuns.companyId,\n        agentId: heartbeatRuns.agentId,\n        contextSnapshot: heartbeatRuns.contextSnapshot,\n      })\n      .from(heartbeatRuns)\n      .where(eq(heartbeatRuns.id, runId))\n      .then((rows) => rows[0] ?? null);\n    if (!run || run.companyId !== companyId || run.agentId !== req.actor.agentId) return true;\n    if (!isStatusOnlyRecoveryContext(run.contextSnapshot)) return true;\n\n    res.status(403).json({\n      error: \"Status-only recovery runs cannot create or modify approvals\",\n      details: {\n        companyId,\n        runId: run.id,\n        recoveryIntent: \"status_only\",\n        resumeRequiresNormalModel: true,\n      },\n    });\n    return false;\n  }\n\n  router.get(\"/companies/:companyId/approvals\", async (req, res) => {\n    const companyId = req.params.companyId as string;\n    assertCompanyAccess(req, companyId);\n    if (!(await assertApprovalAccessAllowed(req, res, companyId))) return;\n    const status = req.query.status as string | undefined;\n    const result = await svc.list(companyId, status);\n    res.json(result.map((approval) => redactApprovalPayload(approval)));","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/paperclipai/paperclip/blob/5716fe907e596ce73501408fc6efdb19fb61edf2/server/src/routes/approvals.ts#L175-L211","documentation":"Permission guard assertApprovalMutationAllowedByRunContext: an agent actor whose run is a status-only recovery run (per its contextSnapshot) may not create or modify approvals, so the guard throws/returns an error response. It fires when a recovery run's agent attempts approval mutations — reserved for normal runs with full approval authority.","triggerScenarios":"Thrown at server/src/routes/approvals.ts:193 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Retry the approval mutation from a normal (non-recovery) run or from board/operator context.","Downgrade the recovery run to status-only actions; request a human or a regular run to handle the approval.","Verify req.actor.runId/contextSnapshot classification is correct if the run is not intended to be status-only."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"5716fe907e596ce73501408fc6efdb19fb61edf2","analyzedAt":"2026-09-02T18:44:00.616Z","contentChangedAt":"2026-09-02T18:44:00.616Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}