{"record":{"id":"e451138972b2cf68","repo":"paperclipai/paperclip","slug":"paperclip-runner-chat-attachment-read-scope-unavailable","errorCode":"paperclip_runner_chat_attachment_read_scope_unavailable","errorMessage":"paperclip_runner_chat_attachment_read_scope_unavailable","messagePattern":"paperclip_runner_chat_attachment_read_scope_unavailable","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/native-runtime/paperclip-runner-tool-authority.ts","lineNumber":296,"sourceCode":"    ) {\n      throw new Error(\"paperclip_runner_tool_not_advertised\");\n    }\n    if (\n      !runnerApiToolsEnabled(\n        this.binding.companyId,\n        this.binding.apiToolsEnabled,\n      ) &&\n      [\"search_api\", \"call_api\"].includes(call.tool)\n    ) {\n      throw new Error(\"paperclip_runner_tool_not_advertised\");\n    }\n    const context = await this.#boundContext();\n    const input = record(call.arguments);\n    if (call.tool === READ_CHAT_ATTACHMENT_TOOL_NAME) {\n      const scope = this.binding.chatAttachmentReadScope;\n      const identityKeys = [\"companyId\", \"issueId\", \"runId\", \"agentId\"] as const;\n      if (!scope || identityKeys.some((key) => scope.options.binding[key] !== this.binding[key])) {\n        throw new Error(\"paperclip_runner_chat_attachment_read_scope_unavailable\");\n      }\n      if (Object.keys(input).some((key) => key !== \"sourceCommentId\" && key !== \"attachmentId\")) {\n        throw new Error(\"paperclip_runner_chat_attachment_read_arguments_invalid\");\n      }\n      return scope.read({\n        sourceCommentId: requiredUuid(input.sourceCommentId),\n        attachmentId: requiredUuid(input.attachmentId),\n      });\n    }\n    if (call.tool === READ_CURRENT_WAKE_COMMENTS_TOOL_NAME) {\n      if (!this.binding.currentWakeComments) {\n        throw new Error(\"paperclip_runner_tool_not_advertised\");\n      }\n      return readCurrentWakeComments(\n        this.db,\n        this.binding.currentWakeComments,\n        input,\n      );","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/native-runtime/paperclip-runner-tool-authority.ts#L278-L314","documentation":"The runner's read_chat_attachment tool requires a chatAttachmentReadScope bound to the exact run identity. This error is thrown when the scope is missing entirely, or its bound identity (companyId, issueId, runId, agentId) does not match the current tool-execution binding — meaning the tool has no authorized channel to read chat attachments right now.","triggerScenarios":"execute() handles a call with call.tool === READ_CHAT_ATTACHMENT_TOOL_NAME and either this.binding.chatAttachmentReadScope is undefined/null, or any of scope.options.binding.companyId/issueId/runId/agentId !== the corresponding this.binding value.","commonSituations":"A runner was constructed without the attachment-read scope wired in (scope not provided at runner startup); a stale runner instance from a previous run/agent tries to read attachments after respawn; test or replay harness invoking the tool without the scope; binding fields changed (e.g., issue reassigned) after scope creation.","solutions":["Ensure chatAttachmentReadScope is provided when constructing the runner binding for runs that may call the attachment tool.","Recreate the runner/binding so the scope's identity matches the current companyId/issueId/runId/agentId.","Restart the native run so a fresh scope is bound to the current run identity.","Check whether the binding was mutated (issue reassignment, run restart) after scope creation and re-bind accordingly."],"exampleFix":"// before\nconst runner = createNativeRunner({ binding: { agentId, issueId, runId, companyId, ... } }); // no chatAttachmentReadScope\n// after\nconst runner = createNativeRunner({ binding: { agentId, issueId, runId, companyId, chatAttachmentReadScope: createChatAttachmentReadScope({ binding: { agentId, issueId, runId, companyId } }), ... } });","handlingStrategy":"type-guard","validationCode":"function hasMatchingScope(binding) {\n  const scope = binding.chatAttachmentReadScope;\n  if (!scope) return false;\n  return [\"companyId\", \"issueId\", \"runId\", \"agentId\"].every(k => scope.options.binding[k] === binding[k]);\n}","typeGuard":"const scopeIsCurrent = (binding) => !!binding.chatAttachmentReadScope && [\"companyId\",\"issueId\",\"runId\",\"agentId\"].every(k => binding.chatAttachmentReadScope.options.binding[k] === binding[k]);","tryCatchPattern":"try {\n  return await authority.execute(call);\n} catch (e) {\n  if (e.message === \"paperclip_runner_chat_attachment_read_scope_unavailable\") {\n    return respawnRunnerWithFreshScope(binding); // restart run with a scope bound to current identity\n  }\n  throw e;\n}","preventionTips":["Always construct runner bindings for attachment-capable runs with chatAttachmentReadScope attached.","Rebuild the scope whenever the run identity changes (restart, reassign, respawn).","Assert scope-binding identity equality in runner startup tests.","Never share runner instances across runs or agents."],"tags":["authorization","scope-mismatch","runner-tools","native-runtime"],"backgroundTag":"permission-denied","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}