{"record":{"id":"3295d43e46d873ac","repo":"paperclipai/paperclip","slug":"action-task-closed","errorCode":"action_task_closed","errorMessage":"Task is closed","messagePattern":"Task is closed","errorType":"http","errorClass":"ToolGatewayHttpError","httpStatus":409,"severity":"error","filePath":"server/src/services/tool-gateway.ts","lineNumber":9433,"sourceCode":"          \"Tool review has expired\",\n          \"action_expired\",\n        );\n      if (\n        !isTestOriginInvocation(invocation) &&\n        signedPayload.executionOnApprove === true\n      ) {\n        const [issue] = await db\n          .select()\n          .from(issues)\n          .where(\n            and(\n              eq(issues.id, invocation.issueId!),\n              eq(issues.companyId, input.companyId),\n            ),\n          )\n          .limit(1);\n        if (!issue || issue.status === \"done\" || issue.status === \"cancelled\")\n          throw new ToolGatewayHttpError(\n            409,\n            \"Task is closed\",\n            \"action_task_closed\",\n          );\n        const session: ToolGatewaySession = {\n          id: `review:${actionRequest.id}`,\n          token: \"\",\n          companyId: input.companyId,\n          agentId: invocation.agentId,\n          runId: invocation.runId,\n          issueId: issue.id,\n          projectId: issue.projectId,\n          createdAt: new Date(),\n          expiresAt: new Date(Date.now() + DEFAULT_SESSION_TTL_MS),\n        };\n        await restoreApprovedActionIdentity(\n          session,\n          signedPayload.identityContextId,","sourceCodeStart":9415,"sourceCodeEnd":9451,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/tool-gateway.ts#L9415-L9451","documentation":"For approved tool reviews that carry executionOnApprove, the gateway verifies the linked issue still exists in the company and is not in a terminal state ('done' or 'cancelled') before executing. A closed task means the approved action no longer has a live context, so it refuses with 409.","triggerScenarios":"Resolving an approved tool action whose invocation.issueId points to an issue with status 'done' or 'cancelled', or an issue id that no longer exists in that company.","commonSituations":"Human approves a parked tool review long after the underlying task finished; issue cancelled while approval pending; cross-company issue id mismatch after a data fix.","solutions":["Re-request the tool review from the active run/issue, then approve that new request","Reopen the issue (or create a successor issue) and re-run the approval flow","Verify invocation.issueId is correct and belongs to the same company"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const issue = await getIssue(invocation.issueId);\nif (!issue || ['done','cancelled'].includes(issue.status)) throw new Error('Issue closed; re-request tool review');","typeGuard":null,"tryCatchPattern":"catch (e) { if (e.code === 'action_task_closed') { await reissueToolActionRequest(currentIssueId); } else throw e; }","preventionTips":["Approve pending tool reviews before closing or cancelling issues","Auto-cancel open tool reviews when their issue reaches a terminal state","Check issue status in the review UI before approving"],"tags":["lifecycle","approval","issue"],"backgroundTag":"invalid-state-transition","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}