Skip to content

OAuth2 Client Credentials (Vanta): provider token error masked as unhandled_oauth2_cc_error with empty payload #6416

Description

@moazGalbat

Description

When creating a connection for an OAUTH2_CC (OAuth2 client-credentials) provider — reproduced with Vanta — and the token exchange against the provider fails, Nango returns a completely opaque error to the caller:

{
  "error": {
    "message": "An unhandled error of type 'oauth2_cc_error' with payload '{}' has occurred",
    "code": "unhandled_oauth2_cc_error",
    "payload": {}
  }
}

The underlying provider error (the real reason Vanta rejected the token request) is logged server-side but stripped from the HTTP response, so there is no way for an integrator to diagnose why the connection failed. The error is also surfaced as unhandled_*, which misrepresents a handled failure path as an unexpected crash.

Root cause

In packages/server/lib/controllers/oauth.controller.ts, OAuthController.oauth2RequestCC (the failing frame in the stack trace below):

const { success, error, response: credentials } = await connectionService.getOauthClientCredentials({ ... });

if (!success || !credentials) {
    void logCtx.error('Error during OAuth2 client credentials creation', { error, provider: config.provider }); // real provider error captured here
    await logCtx.failed();

    errorManager.errRes(res, 'oauth2_cc_error'); // but only this bare string is returned
    return;
}

Two problems:

  1. The underlying error is discarded from the response. It is passed to logCtx.error(...) but not to errorManager.errRes(...), so the caller never sees Vanta's actual token-endpoint response (status / body / message).
  2. 'oauth2_cc_error' is not a registered error type, so errorManager.errRes falls through to its generic wrapper — "An unhandled error of type 'oauth2_cc_error' with payload '{}'" — producing the misleading unhandled_ code and empty payload.

Possible underlying cause (secondary)

Because the real error is masked we can't confirm it, but the vanta provider definition in packages/providers/providers.yaml sets no scope / scope_separator and no default scopes:

vanta:
    auth_mode: OAUTH2_CC
    token_url: https://api.vanta.com/oauth/token
    token_params:
        grant_type: client_credentials
    body_format: json

Vanta's client-credentials grant requires a scope parameter (e.g. vanta-api.all:read). If the token request goes out without a scope, Vanta returns a 4xx — which is exactly the kind of error currently being swallowed. Surfacing the real error (problem #1) would confirm this.

Steps to reproduce

  1. Configure a vanta integration in Nango with valid Vanta OAuth client credentials.
  2. Start a Connect session and POST to the OAuth2 CC auth endpoint:
curl 'https://api.nango.dev/oauth2/auth/vanta?connect_session_token=<token>' \
  -H 'content-type: application/json' \
  --data-raw '{"client_id":"<real_id>","client_secret":"<real_secret>"}'
  1. Observe the opaque unhandled_oauth2_cc_error response with empty payload.

Expected behavior

The response should include the underlying provider error — at minimum the HTTP status and body returned by Vanta's token endpoint — so the failure is diagnosable, and it should not be classified as an unhandled_ error.

Actual behavior

Caller receives unhandled_oauth2_cc_error with payload: {}. The real cause is only visible in server logs (and even there, as a generic wrapped error).

Environment

  • Nango: reproduced on self-hosted (Docker; server path file:///app/nango/...) and Nango Cloud (api.nango.dev).
  • Provider: vanta (auth_mode: OAUTH2_CC).
  • Flow: Connect session → POST /oauth2/auth/vanta.

Server logs

12:52:34.073 error (ErrorManager) Response error An unhandled error of type 'oauth2_cc_error' with payload '{}' has occurred {
  additional_properties: undefined,
  message: "An unhandled error of type 'oauth2_cc_error' with payload '{}' has occurred",
  name: 'Error',
  payload: {},
  stack: "Error: An unhandled error of type 'oauth2_cc_error' with payload '{}' has occurred
      at ErrorManager.errRes (file:///app/nango/packages/shared/dist/utils/error.manager.js:56:21)
      at OAuthController.oauth2RequestCC (file:///app/nango/packages/server/dist/controllers/oauth.controller.js:360:30)
      at process.processTicksAndRejections (node:internal/process/task_queues:105:5)",
  status: 500,
  type: 'unhandled_oauth2_cc_error'
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions