{"record":{"id":"8a561bf274c48ab6","repo":"paperclipai/paperclip","slug":"paperclip-run-authentication-is-unavailable","errorCode":null,"errorMessage":"Paperclip run authentication is unavailable","messagePattern":"Paperclip run authentication is unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"server/src/services/native-runtime/paperclip-runner-tool-authority.ts","lineNumber":249,"sourceCode":"      }\n      case \"report_progress\": return this.#reportProgress(input);\n      case \"request_human_input\": return this.#requestHumanInput(input,\n        (await captureRunIdentity(this.db, this.binding)).context?.id ?? null);\n      case \"create_task\": return this.#createTask(input,\n        (await captureRunIdentity(this.db, this.binding)).context?.id ?? null);\n      case \"set_dependencies\": return this.#setDependencies(input);\n      default: throw new Error(\"paperclip_runner_tool_not_bound\");\n    }\n  }\n\n  async #callApi(callId: string, value: unknown): Promise<unknown> {\n    const bound = await this.#boundContext();\n    const context = { ...this.binding, issueIdentifier: bound.issue.identifier, workMode: bound.issue.workMode };\n    const { input, operation } = validateRunnerApiCall(value, context);\n    const apiUrl = this.binding.apiUrl ?? process.env.PAPERCLIP_API_URL;\n    if (!apiUrl) throw new Error(\"Paperclip API origin is unavailable\");\n    const token = createLocalAgentJwt(this.binding.agentId, this.binding.companyId, bound.actor.adapterType, this.binding.runId, bound.run.responsibleUserId);\n    if (!token) throw new Error(\"Paperclip run authentication is unavailable\");\n    const execute = async () => {\n      const current = await this.#boundContext();\n      if (!runnerApiToolsEnabled(this.binding.companyId, this.binding.apiToolsEnabled)) throw new Error(\"paperclip_runner_tool_not_advertised\");\n      return executeRunnerApi(input, { ...context, workMode: current.issue.workMode }, {\n        apiUrl, token,\n        beforeDispatch: async () => {\n          const fresh = await this.#boundContext();\n          if (!runnerApiToolsEnabled(this.binding.companyId, this.binding.apiToolsEnabled)) throw new Error(\"paperclip_runner_tool_not_advertised\");\n          validateRunnerApiCall(input, { ...context, workMode: fresh.issue.workMode });\n        },\n        readFile: (file) => this.#readApiFile(file),\n        saveResponse: async (bytes, contentType) => {\n          const storage = this.binding.storage ?? getStorageService();\n          const saved = await storage.putFile({ companyId: this.binding.companyId, namespace: \"runner-api\", originalFilename: contentType.includes(\"json\") ? \"response.json\" : \"response.bin\", contentType, body: bytes });\n          const asset = await assetService(this.db).create(this.binding.companyId, { ...saved, createdByAgentId: this.binding.agentId });\n          const activity = await persistActivity(this.db, { companyId: this.binding.companyId, actorType: \"agent\", actorId: this.binding.agentId, agentId: this.binding.agentId, runId: this.binding.runId, issueId: this.binding.issueId, action: \"asset.created\", entityType: \"asset\", entityId: asset.id, details: { source: \"runner.call_api\", byteSize: saved.byteSize } });\n          publishActivity(activity.publication);\n          return { artifactId: asset.id, url: `/api/assets/${asset.id}/content`, contentType, byteSize: saved.byteSize, sha256: saved.sha256 };","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/native-runtime/paperclip-runner-tool-authority.ts#L231-L267","documentation":"PaperclipRunnerToolAuthority.#callApi mints a short-lived local agent JWT (createLocalAgentJwt) before dispatching any runner API call. createLocalAgentJwt returns null when jwtConfig() cannot produce a signing configuration (missing JWT secret/keys), so the authority throws this error instead of sending an unauthenticated request. It is a fail-closed guard: no run authentication material means the runner tool cannot call the Paperclip API at all.","triggerScenarios":"An agent tool call to search_api/call_api during a native run where createLocalAgentJwt(agentId, companyId, adapterType, runId, responsibleUserId) returns null — i.e. the server's JWT signing config is absent (jwtConfig() falsy), typically because no JWT secret is configured on the instance.","commonSituations":"Self-hosted/dev instances started without JWT configuration in env or config file; config was reset or migrated and the signing key was dropped; running the runner authority in a test harness where jwtConfig() is not initialized.","solutions":["Configure the server's JWT signing configuration (secret/keys) so jwtConfig() returns a valid config, then restart the API server.","Verify PAPERCLIP_API_URL (or binding.apiUrl) is also set, since the preceding check requires an API origin before token minting.","Check server startup logs for JWT config initialization warnings and re-run the deployment/config setup steps.","If running tests, initialize the JWT config in the test setup before exercising runner API tools."],"exampleFix":"// before\n// server started with no JWT secret; createLocalAgentJwt() -> null\n// after\n// .env\nPAPERCLIP_JWT_SECRET=<generated-256-bit-secret>\n// restart server, then the runner tool can mint the local agent JWT","handlingStrategy":"try-catch","validationCode":"import { jwtConfig } from \"../agent-auth-jwt.js\";\nconst canMint = typeof jwtConfig === \"function\" && jwtConfig() != null; // run before enabling runner tools\nif (!canMint || !process.env.PAPERCLIP_API_URL) throw new Error(\"Runner API prerequisites missing: JWT config and PAPERCLIP_API_URL required\");","typeGuard":"function hasRunAuth(t: string | null | undefined): t is string { return typeof t === \"string\" && t.length > 0; }","tryCatchPattern":"try {\n  const result = await authority.callApi(input);\n} catch (err) {\n  if (err instanceof Error && err.message === \"Paperclip run authentication is unavailable\") {\n    // surface a config error: JWT signing config missing on the server\n  } else throw err;\n}","preventionTips":["Include JWT config presence in server startup health checks so misconfigured instances never serve runner tools.","Verify JWT config in your deployment smoke test before enabling PAPERCLIP_RUNNER_API_TOOLS.","Keep the JWT secret in managed secret storage and assert it exists during config validation."],"tags":["authentication","jwt","configuration","native-runtime"],"backgroundTag":"authentication-required","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"}