paperclipai/paperclip · error · ToolGatewayHttpError

github_identity_unavailable

github_identity_unavailable

Error message

No GitHub identity connected

What it means

The Paperclip tool gateway resolves a managed GitHub identity for the session before dispatching a call through a connection whose source template is "github". If resolveManagedGitHubIdentitySelection finds no identity grant for the company/responsible user (and standing delegation is disallowed), the gateway aborts with HTTP 409 and code github_identity_unavailable. It means the agent session has no usable GitHub identity to act as.

Source

Thrown at server/src/services/tool-gateway.ts:2078

  async function findToolForSession(session: ToolGatewaySession, toolName: string): Promise<ToolGatewayDescriptor> {
    const connectedTools = await connectedMcpToolsForCompany(session.companyId);
    const hasOnDemandTargets = connectedTools.some(isOnDemandRemoteTool);
    const virtualTools = hasOnDemandTargets ? VIRTUAL_TOOLS : [];
    const tool = [...allTools(), ...connectedTools, ...virtualTools]
      .filter((candidate) => session.agentId || (candidate.providerType !== "paperclip_self" && candidate.providerType !== "paperclip_plugin"))
      .find((candidate) => candidate.name === toolName);
    if (!tool) {
      throw new ToolGatewayHttpError(404, `Tool "${toolName}" not found`, "tool_not_found", { tool: toolName });
    }
    if (session.identityContextId && session.agentId && tool.connectionId) {
      const [connection] = await db.select().from(toolConnections).where(and(
        eq(toolConnections.id, tool.connectionId), eq(toolConnections.companyId, session.companyId),
      ));
      if (connection?.config.sourceTemplateKey === "github" || connection?.transportConfig?.sourceTemplateKey === "github") {
        let selected = await resolveManagedGitHubIdentitySelection(db, session.companyId, {
          agentId: session.agentId, responsibleUserId: session.responsibleUserId, allowStandingDelegation: false,
        });
        if (!selected.grant) throw new ToolGatewayHttpError(409, selected.error ?? "No GitHub identity connected", "github_identity_unavailable");
        const original = selected.grant;
        // Acquire before policy evaluation or dispatch. An alternate connection
        // gets its own catalog descriptor and policy checks; never replay a call.
        for (let attempt = 0; attempt < 2; attempt += 1) {
          const grant = selected.grant!;
          const target = connectedTools.find((candidate) => candidate.connectionId === grant.connectionId
            && candidate.upstreamToolName === tool.upstreamToolName && candidate.providerType === tool.providerType);
          if (!target) throw new ToolGatewayHttpError(404, "This GitHub tool is unavailable for the responsible person", "github_tool_unavailable");
          const [selectedConnection] = await db.select().from(toolConnections).where(and(
            eq(toolConnections.id, grant.connectionId), eq(toolConnections.companyId, session.companyId),
          ));
          if (!selectedConnection) throw new ToolGatewayHttpError(409, "GitHub connection is unavailable", "github_identity_unavailable");
          if (attempt === 0) await resolveConnectionGrant(session, selectedConnection);
          try {
            const headers = await resolveCredentialHeaders(session, selectedConnection, grant);
            githubOperationCredentials.set(session, { grant, headers });
            return target;
          } catch (error) {

View on GitHub (pinned to 01ad858492)

Solutions

  1. Connect a managed GitHub identity for the company/responsible user via the Apps catalog GitHub connection flow before dispatching the tool call.
  2. Verify the responsibleUserId on the session actually has a linked GitHub grant; re-link if it was revoked.
  3. If delegation is appropriate, allow standing delegation in resolveManagedGitHubIdentitySelection or grant the agent its own identity.
  4. Check the message body for the underlying selected.error detail to distinguish 'not connected' from 'connected but not selectable'.
  5. Return a clear operator action in the UI: prompt the responsible user to connect GitHub under Apps.

Example fix

// before
const res = await gateway.call(session, "github_list_issues", args); // throws 409
// after
const sel = await resolveManagedGitHubIdentitySelection(db, session.companyId, { agentId: session.agentId, responsibleUserId: session.responsibleUserId, allowStandingDelegation: false });
if (!sel.grant) throw new Error(`Connect GitHub first: ${sel.error}`);
const res = await gateway.call(session, "github_list_issues", args);
Defensive patterns

Strategy: validation

When it happens

Trigger: An agent invokes a GitHub-backed tool via the gateway where toolConnections row has config.sourceTemplateKey (or transportConfig.sourceTemplateKey) === "github", and resolveManagedGitHubIdentitySelection returns no grant: no identity is connected for the responsible user, no agent-scoped grant exists, and allowStandingDelegation is false so delegation cannot substitute. selected.error is included in the message when present.

Common situations: Agent tries a GitHub tool before anyone connected GitHub in Apps/connections for that company; the responsible user changed to someone without a linked GitHub identity; a grant was revoked/deleted; org setup expects standing delegation but the call path passes allowStandingDelegation:false.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/27e587293c85dcc0. Report an issue: GitHub.