{"record":{"id":"ebf5718f6b2ada1f","repo":"paperclipai/paperclip","slug":"github-tool-unavailable","errorCode":"github_tool_unavailable","errorMessage":"This GitHub tool is unavailable for the responsible person","messagePattern":"This GitHub tool is unavailable for the responsible person","errorType":"http","errorClass":"ToolGatewayHttpError","httpStatus":404,"severity":"error","filePath":"server/src/services/tool-gateway.ts","lineNumber":2086,"sourceCode":"      throw new ToolGatewayHttpError(404, `Tool \"${toolName}\" not found`, \"tool_not_found\", { tool: toolName });\n    }\n    if (session.identityContextId && session.agentId && tool.connectionId) {\n      const [connection] = await db.select().from(toolConnections).where(and(\n        eq(toolConnections.id, tool.connectionId), eq(toolConnections.companyId, session.companyId),\n      ));\n      if (connection?.config.sourceTemplateKey === \"github\" || connection?.transportConfig?.sourceTemplateKey === \"github\") {\n        let selected = await resolveManagedGitHubIdentitySelection(db, session.companyId, {\n          agentId: session.agentId, responsibleUserId: session.responsibleUserId, allowStandingDelegation: false,\n        });\n        if (!selected.grant) throw new ToolGatewayHttpError(409, selected.error ?? \"No GitHub identity connected\", \"github_identity_unavailable\");\n        const original = selected.grant;\n        // Acquire before policy evaluation or dispatch. An alternate connection\n        // gets its own catalog descriptor and policy checks; never replay a call.\n        for (let attempt = 0; attempt < 2; attempt += 1) {\n          const grant = selected.grant!;\n          const target = connectedTools.find((candidate) => candidate.connectionId === grant.connectionId\n            && candidate.upstreamToolName === tool.upstreamToolName && candidate.providerType === tool.providerType);\n          if (!target) throw new ToolGatewayHttpError(404, \"This GitHub tool is unavailable for the responsible person\", \"github_tool_unavailable\");\n          const [selectedConnection] = await db.select().from(toolConnections).where(and(\n            eq(toolConnections.id, grant.connectionId), eq(toolConnections.companyId, session.companyId),\n          ));\n          if (!selectedConnection) throw new ToolGatewayHttpError(409, \"GitHub connection is unavailable\", \"github_identity_unavailable\");\n          if (attempt === 0) await resolveConnectionGrant(session, selectedConnection);\n          try {\n            const headers = await resolveCredentialHeaders(session, selectedConnection, grant);\n            githubOperationCredentials.set(session, { grant, headers });\n            return target;\n          } catch (error) {\n            if (attempt !== 0) throw error;\n            const alternate = await resolveManagedGitHubIdentitySelection(db, session.companyId, {\n              agentId: session.agentId, responsibleUserId: session.responsibleUserId,\n              allowStandingDelegation: false, excludeGrantId: original.id,\n            });\n            const accountId = original.providerTenant?.github?.userId;\n            if (!accountId || !alternate.grant\n              || alternate.grant.providerTenant?.github?.userId !== accountId","sourceCodeStart":2068,"sourceCodeEnd":2104,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/tool-gateway.ts#L2068-L2104","documentation":"After an identity grant is selected, the gateway looks for a connected tool row matching the grant's connection, the requested upstreamToolName, and providerType. If none exists, the specific GitHub tool is not available to the responsible person's selected identity, so dispatch is refused with HTTP 404 github_tool_unavailable.","triggerScenarios":"Retry loop (attempt 0 or 1) where selected.grant.connectionId points at a connection whose catalog has no matching connectedTools entry with identical upstreamToolName and providerType — e.g. the alternate identity's connection doesn't expose that tool, the tool name changed upstream, or the tool was removed from the connection's descriptor.","commonSituations":"Calling a GitHub tool that exists on one connection but not the alternate identity's connection; a GitHub app scopes/tools changed; typo'd or renamed upstream tool name; providerType mismatch after adapter updates.","solutions":["List the connectedTools for grant.connectionId and confirm the upstreamToolName/providerType exist for that identity's connection.","Use a tool name supported by the selected identity's GitHub connection; fall back to the original connection if the alternate lacks the tool.","Refresh the connection's tool catalog/descriptor so newly supported GitHub tools appear.","Check for upstream renames (e.g. tool renamed between GitHub app versions) and update the requested name."],"exampleFix":"// before\nawait gateway.call(session, \"github_create_gist\", args); // 404 if unavailable for this identity\n// after\nconst available = connectedTools.some(t => t.upstreamToolName === \"github_create_gist\" && t.providerType === provider);\nif (!available) throw new Error(\"github_create_gist not available on the selected GitHub connection\");\nawait gateway.call(session, \"github_create_gist\", args);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":["github","tool-gateway","resource-not-found"],"backgroundTag":"tool-not-available-for-identity","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"}