paperclipai/paperclip · error · ToolGatewayHttpError
identity_context_unavailable
identity_context_unavailable
Error message
Approved action identity is unavailable
What it means
Before executing an approved action, the gateway restores the identity context that was accepted at approval time. It looks up run_identity_contexts by id, company, run, and status 'accepted'; if no such accepted row exists, the approved identity cannot be re-established and execution is refused with a 409.
Source
Thrown at server/src/services/tool-gateway.ts:5578
.where(eq(toolActionRequests.id, claimed.id));
await reflectToolActionInteractionLifecycle({ actionRequestId: claimed.id, status: "expired" });
throw new ToolGatewayHttpError(
409,
"The issue for this tool action is closed; the approval has expired",
"action_issue_closed",
{ actionRequestId: claimed.id, invocationId: invocation.id },
);
}
async function restoreApprovedActionIdentity(session: ToolGatewaySession, identityContextId: string | undefined) {
if (!identityContextId) return;
const [origin] = await db.select().from(runIdentityContexts).where(and(
eq(runIdentityContexts.id, identityContextId),
eq(runIdentityContexts.companyId, session.companyId),
eq(runIdentityContexts.runId, session.runId!),
eq(runIdentityContexts.status, "accepted"),
));
if (!origin) throw new ToolGatewayHttpError(409, "Approved action identity is unavailable", "identity_context_unavailable");
session.identityContextId = origin.id;
session.responsibleUserId = origin.cause === "company_default" ? null : origin.responsibleUserId;
}
async function executeApprovedAgentInvocation(input: {
actionRequest: typeof toolActionRequests.$inferSelect;
invocation: typeof toolInvocations.$inferSelect;
}) {
const { actionRequest, invocation } = input;
if (!invocation.agentId || !invocation.issueId || isTestOriginInvocation(invocation)) {
throw new ToolGatewayHttpError(409, "Tool action request is not an agent-origin action", "action_origin_invalid");
}
const [claimed] = await db
.update(toolActionRequests)
.set({ status: "executing", updatedAt: new Date() })
.where(and(eq(toolActionRequests.id, actionRequest.id), eq(toolActionRequests.status, "approved")))
.returning();View on GitHub (pinned to 01ad858492)
Solutions
- Re-run the approval flow so a fresh accepted run_identity_contexts row is created, then approve again
- Verify the identityContextId in the signed payload matches the current run (not an old run's context)
- Check run_identity_contexts status: re-accept the pending identity context before approving
Example fix
// before: stale context id
await restoreApprovedActionIdentity(session, staleIdentityContextId);
// after: re-request approval with current accepted context
const [origin] = await db.select().from(runIdentityContexts).where(eq(runIdentityContexts.runId, session.runId));
if (origin?.status !== 'accepted') throw new Error('Re-approve: identity context not accepted'); Defensive patterns
Strategy: validation
Validate before calling
const [ctx] = await db.select().from(runIdentityContexts).where(and(eq(runIdentityContexts.id, identityContextId), eq(runIdentityContexts.status, 'accepted')));
if (!ctx) throw new Error('Identity context not accepted; re-run approval'); Prevention
- Never reuse signed payloads across runs or identity resets
- Re-request approval whenever the identity context changes status
- Avoid deleting run_identity_contexts rows while actions are pending
When it happens
Trigger: executeApprovedAgentInvocation path calls restoreApprovedActionIdentity with an identityContextId that is missing, belongs to another company/run, or whose status is not 'accepted' (pending, rejected, or reset).
Common situations: Identity context was reset/re-requested after approval (status changed), run was restarted with new identity contexts, stale signed payload replayed after company data moved, or the row was deleted by cleanup.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- OpenCode target executable is not a regular file
- ACPX session is not at a safe suspension point
- OpenCode thread is not open
- Warm run transition binding is invalid.
- Durable authority commit is indeterminate; reload is require
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/b88ddfadfe005e57.
Report an issue: GitHub.