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:
- 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).
'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
- Configure a
vanta integration in Nango with valid Vanta OAuth client credentials.
- 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>"}'
- 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'
}
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):Two problems:
erroris discarded from the response. It is passed tologCtx.error(...)but not toerrorManager.errRes(...), so the caller never sees Vanta's actual token-endpoint response (status / body / message).'oauth2_cc_error'is not a registered error type, soerrorManager.errResfalls through to its generic wrapper — "An unhandled error of type 'oauth2_cc_error' with payload '{}'" — producing the misleadingunhandled_code and empty payload.Possible underlying cause (secondary)
Because the real error is masked we can't confirm it, but the
vantaprovider definition inpackages/providers/providers.yamlsets noscope/scope_separatorand no default scopes:Vanta's client-credentials grant requires a
scopeparameter (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
vantaintegration in Nango with valid Vanta OAuth client credentials.POSTto the OAuth2 CC auth endpoint:unhandled_oauth2_cc_errorresponse 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_errorwithpayload: {}. The real cause is only visible in server logs (and even there, as a generic wrapped error).Environment
file:///app/nango/...) and Nango Cloud (api.nango.dev).vanta(auth_mode: OAUTH2_CC).POST /oauth2/auth/vanta.Server logs