{"record":{"id":"f5340dace10e3a63","repo":"paperclipai/paperclip","slug":"project-tool-authentication-is-unavailable","errorCode":null,"errorMessage":"Project tool authentication is unavailable","messagePattern":"Project tool authentication is unavailable","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/native-runtime/paperclip-runner-tool-authority.ts","lineNumber":357,"sourceCode":"      !descriptor.allowedModes.includes(\n        context.issue.workMode as \"standard\" | \"planning\" | \"ask\",\n      )\n    ) {\n      throw new Error(\"paperclip_runner_tool_mode_denied\");\n    }\n    switch (call.tool) {\n      case \"create_skill\": {\n        const apiUrl = this.binding.apiUrl ?? process.env.PAPERCLIP_API_URL;\n        const token = createLocalAgentJwt(this.binding.agentId, this.binding.companyId, context.actor.adapterType, this.binding.runId, context.run.responsibleUserId);\n        if (!apiUrl || !token) throw new Error(\"Skill tool authentication is unavailable\");\n        return callCreateSkillTool({ arguments: input, apiUrl, token, companyId: this.binding.companyId });\n      }\n      case \"create_project\":\n      case \"list_project_repositories\":\n      case \"list_projects\": {\n        const apiUrl = this.binding.apiUrl ?? process.env.PAPERCLIP_API_URL;\n        const token = createLocalAgentJwt(this.binding.agentId, this.binding.companyId, context.actor.adapterType, this.binding.runId, context.run.responsibleUserId);\n        if (!apiUrl || !token) throw new Error(\"Project tool authentication is unavailable\");\n        return callProjectTool({ name: call.tool, arguments: input, apiUrl, token,\n          companyId: this.binding.companyId, issueId: this.binding.issueId, agentId: this.binding.agentId,\n          conversation: Boolean(context.issue.conversationAgentId) });\n      }\n      case \"search_api\": return searchRunnerApi(call.arguments);\n      case \"call_api\": {\n        // PRP reserves operationId/callId for semantic result identity. The\n        // HTTP operation is metadata, including in previously saved receipts;\n        // exposing it as operationId makes the runner reject a valid response.\n        const { operationId, ...response } = record(await this.#callApi(call.callId, call.arguments));\n        return { ...response, apiOperationId: operationId };\n      }\n      case \"get_task_context\": return {\n        company: { id: this.binding.companyId },\n        actor: redactedActor(context.actor),\n        activeTask: redactedTask(context.issue),\n        run: {\n          id: this.binding.runId,","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/native-runtime/paperclip-runner-tool-authority.ts#L339-L375","documentation":"The project tools (create_project, list_project_repositories, list_projects) call back into the Paperclip API using binding.apiUrl ?? PAPERCLIP_API_URL and a locally minted agent JWT. This error means the API URL or token could not be produced, so the project tool cannot make an authenticated request.","triggerScenarios":"execute() routes call.tool to the create_project/list_project_repositories/list_projects case and either apiUrl (binding.apiUrl ?? process.env.PAPERCLIP_API_URL) is falsy or createLocalAgentJwt(this.binding.agentId, this.binding.companyId, context.actor.adapterType, this.binding.runId, context.run.responsibleUserId) returns an empty token.","commonSituations":"Runner process started without PAPERCLIP_API_URL (local CLI, container missing env); binding constructed without apiUrl; run row missing responsibleUserId so token creation fails; agent/agentId context incomplete in test harnesses.","solutions":["Set PAPERCLIP_API_URL in the runner environment or pass binding.apiUrl.","Ensure the run/actor context includes responsibleUserId and adapterType so createLocalAgentJwt succeeds.","Confirm binding.agentId and binding.companyId are set when constructing the tool authority.","In containerized runners, propagate the Paperclip API env/config at launch time."],"exampleFix":"// before\nconst authority = new RunnerToolAuthority({ db, binding: { agentId, issueId, runId } }); // no apiUrl\n// after\nconst authority = new RunnerToolAuthority({ db, binding: { agentId, issueId, runId, apiUrl: process.env.PAPERCLIP_API_URL } });","handlingStrategy":"validation","validationCode":"function assertProjectToolAuth(binding) {\n  const apiUrl = binding.apiUrl ?? process.env.PAPERCLIP_API_URL;\n  if (!apiUrl) throw new Error(\"PAPERCLIP_API_URL must be set for project tools\");\n  return apiUrl;\n}","typeGuard":"const hasProjectAuth = (binding) => Boolean(binding.apiUrl ?? process.env.PAPERCLIP_API_URL);","tryCatchPattern":"try {\n  return await authority.execute(call);\n} catch (e) {\n  if (e.message === \"Project tool authentication is unavailable\") {\n    return respondSkipped(\"Project tools require PAPERCLIP_API_URL; fix runner config and retry.\");\n  }\n  throw e;\n}","preventionTips":["Propagate PAPERCLIP_API_URL into runner containers/CLI launches.","Set binding.apiUrl at authority construction instead of depending on process env.","Verify createLocalAgentJwt inputs (agentId, companyId, adapterType, runId, responsibleUserId) before enabling project tools.","Add a startup probe that mints a test JWT to catch config drift early."],"tags":["authentication","missing-env-var","runner-tools","project-tools"],"backgroundTag":"missing-env-var","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"}