{"record":{"id":"5bd2b0a0fce00d79","repo":"paperclipai/paperclip","slug":"paperclip-runner-approval-not-found","errorCode":"paperclip_runner_approval_not_found","errorMessage":"paperclip_runner_approval_not_found","messagePattern":"paperclip_runner_approval_not_found","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/native-runtime/paperclip-runner-tool-authority.ts","lineNumber":350,"sourceCode":"    const handle = await openRunnerApiWorkspaceFile(resolved.realPath);\n    try {\n      const stat = await handle.stat();\n      if (!stat.isFile() || stat.size > RUNNER_API_MAX_BYTES) throw badRequest(\"Workspace file exceeds API transfer limit\");\n      const bytes = Buffer.alloc(RUNNER_API_MAX_BYTES + 1);\n      let length = 0;\n      while (length < bytes.length) {\n        const { bytesRead } = await handle.read(bytes, length, bytes.length - length, length);\n        if (!bytesRead) break;\n        length += bytesRead;\n      }\n      if (length > RUNNER_API_MAX_BYTES) throw badRequest(\"Workspace file exceeds API transfer limit\");\n      return { bytes: bytes.subarray(0, length), filename: basename(resolved.realPath), contentType: \"application/octet-stream\" };\n    } finally { await handle.close(); }\n  }\n\n  async #approval(id: string) {\n    const approval = await approvalService(this.db).getById(id);\n    if (!approval || approval.companyId !== this.binding.companyId) throw new Error(\"paperclip_runner_approval_not_found\");\n    return approval;\n  }\n\n  async #boundContext() {\n    const [row] = await this.db.select({ issue: issues, actor: agents, run: heartbeatRuns })\n      .from(heartbeatRuns)\n      .innerJoin(issues, eq(issues.id, this.binding.issueId))\n      .innerJoin(agents, eq(agents.id, this.binding.agentId))\n      .where(and(\n        eq(heartbeatRuns.id, this.binding.runId),\n        eq(heartbeatRuns.companyId, this.binding.companyId),\n        eq(heartbeatRuns.agentId, this.binding.agentId),\n        eq(heartbeatRuns.nativeIssueId, this.binding.issueId),\n        eq(issues.companyId, this.binding.companyId),\n        eq(issues.assigneeAgentId, this.binding.agentId),\n        eq(issues.executionRunId, this.binding.runId),\n        eq(agents.companyId, this.binding.companyId),\n      ))","sourceCodeStart":332,"sourceCodeEnd":368,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/native-runtime/paperclip-runner-tool-authority.ts#L332-L368","documentation":"PaperclipRunnerToolAuthority.#approval loads an approval record by id via approvalService(db).getById(id) and throws paperclip_runner_approval_not_found when no row exists or the row belongs to a different company than the run binding. This enforces company scoping: a run may only resolve approval gates that belong to its own company.","triggerScenarios":"A runner tool call references an approval id that (a) does not exist in the approvals table, (b) was deleted/expired, or (c) exists under a different companyId than this.binding.companyId.","commonSituations":"Agent passes a stale or fabricated approval id; cross-company id replay (id copied from another company's run); approval was revoked between issuance and the tool call; typo/truncation of the id by the calling agent.","solutions":["Re-issue a fresh approval for this company/issue and use the new id in the runner tool call.","Verify the approval id belongs to the same company as the run binding (query the approvals table for companyId).","Check whether the approval was deleted or expired and request a new one through the normal approval flow.","If the id comes from an earlier message/tool result, re-fetch current pending approvals instead of reusing cached ids."],"exampleFix":"// before\nawait authority.approval(\"appr_from_other_company\");\n// after\nconst pending = await listApprovals({ companyId: binding.companyId, issueId });\nawait authority.approval(pending[0].id);","handlingStrategy":"validation","validationCode":"const approval = await approvalService(db).getById(id);\nif (!approval || approval.companyId !== binding.companyId) {\n  throw new Error(\"approval is missing or belongs to another company — request a fresh approval for this run\");\n}","typeGuard":"function isOwnCompanyApproval(a: { companyId: string } | null | undefined, companyId: string): a is { companyId: string } {\n  return !!a && a.companyId === companyId;\n}","tryCatchPattern":"try {\n  const approval = await authority.approval(id);\n} catch (err) {\n  if (err instanceof Error && err.message === \"paperclip_runner_approval_not_found\") {\n    // list current pending approvals for this company/issue and use a fresh id\n  } else throw err;\n}","preventionTips":["Always source approval ids from a live listing of pending approvals, never from cached tool transcripts.","Never reuse approval ids across companies or issues.","Treat approvals as single-use/expiring and re-request after revocation or expiry."],"tags":["approvals","resource-not-found","company-scope","native-runtime"],"backgroundTag":"record-not-found","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}