paperclipai/paperclip · error · HttpError

oauth_token_exchange_failed

oauth_token_exchange_failed

Error message

OAuth token exchange failed

What it means

Generic OAuth token-exchange failure: the token endpoint returned non-2xx (or ok:false) and the grant was not a recoverable invalid_grant. Wrapped as HttpError 502 with providerError/status for diagnostics; the provider's token endpoint is at fault.

Source

Thrown at server/src/services/tool-access.ts:4605

        // one credential a caller could still present.
        await db
          .update(toolProfiles)
          .set({ status: "archived", defaultAction: "deny", updatedAt: now })
          .where(eq(toolProfiles.id, appProfile.id));
        appProfileOutcome = "archived";
        const revokedTokens = await db
          .update(toolMcpGatewayTokens)
          .set({ revokedAt: now, updatedAt: now })
          .where(and(
            eq(toolMcpGatewayTokens.companyId, connection.companyId),
            inArray(toolMcpGatewayTokens.gatewayId, gatewayRows.map((row) => row.id)),
            isNull(toolMcpGatewayTokens.revokedAt),
          ))
          .returning({ id: toolMcpGatewayTokens.id });
        gatewayTokensRevoked = revokedTokens.length;
        if (revokedTokens.length > 0) {
          gatewaySessionsRevoked = (await db
            .update(toolGatewaySessions)
            .set({ revokedAt: now, updatedAt: now })
            .where(and(
              eq(toolGatewaySessions.companyId, connection.companyId),
              inArray(toolGatewaySessions.gatewayTokenId, revokedTokens.map((row) => row.id)),
              isNull(toolGatewaySessions.revokedAt),
            ))
            .returning({ id: toolGatewaySessions.id })).length;
        }
      }
    }

    // A local runtime already holds the injected credential inside a live child
    // process, so archiving rows is not enough — the process itself is an access
    // path. Stopping is best effort on purpose: if the supervisor cannot be
    // reached, revoking the credential anyway (so nothing can start again) beats
    // abandoning the teardown, and the warning says which slot was left running.
    let runtimeSlotsStopped = 0;
    const runtimeSlotRows = await db

View on GitHub (pinned to 01ad858492)

Solutions

  1. The token exchange with the provider failed. Verify client credentials, redirect URI match, and provider availability, then reconnect the app.
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at server/src/services/tool-access.ts:4447 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-08-18). Data as JSON: /api/errors/dda65add5d3acdb6. Report an issue: GitHub.