n8n - ✅(Solved) Fix Databricks OAuth2: token refresh is hardcoded to 401, cannot handle 403 [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
n8n-io/n8n#28832Fetched 2026-04-22 08:03:04
View on GitHub
Comments
1
Participants
2
Timeline
7
Reactions
0
Author
Timeline (top)
labeled ×3commented ×1cross-referenced ×1mentioned ×1

Error Message

error bubbles up to the user instead of triggering a fresh

  • error: all

Fix Action

Fixed

PR fix notes

PR #28833: fix(Databricks Node): Expose token expired status code on OAuth2 credential

Description (problem / solution / changelog)

Summary

The base oAuth2Api credential defines tokenExpiredStatusCode with doNotInherit: true, so extending credentials never surface the field in the UI. For Databricks OAuth2, this means credentials.tokenExpiredStatusCode is always undefined at runtime, and resolveTokenExpiredStatusCode in packages/core/src/execution-engine/node-execution-context/utils/request-helper-functions.ts falls back to 401. Any 403 (or other non-401) response from Databricks therefore skips the refresh path in requestOAuth2 and fails instead of transparently minting a new token.

This PR redeclares tokenExpiredStatusCode on DatabricksOAuth2Api so users whose deployments signal token expiry with a non-401 status can override the default. Default stays at 401, preserving current behavior for everyone else.

Before / After

  • Before: field absent from DatabricksOAuth2Api; status code hardcoded to 401 via fallback; no refresh on 403.
  • After: field present on the credential with default 401; users can set it to 403 (or anything else) if needed; existing credentials keep the 401 default.

How to test

  1. Create a Databricks OAuth2 credential — the new Token Expired Status Code field should appear with default 401.
  2. Execute any Databricks node operation — behavior should be unchanged for deployments that return 401 on expired tokens.
  3. For deployments that return 403: set the field to 403, let the cached token expire, re-execute — n8n should mint a new client_credentials token and retry the request once.

Related Linear tickets, Github issues, and Community forum posts

Fixes #28832

Review / Merge checklist

  • I have seen this code, I have run this code, and I take responsibility for this code.
  • PR title and summary are descriptive.
  • Docs updated or follow-up ticket created.
  • Tests included.
  • PR Labeled with Backport to Beta, Backport to Stable, or Backport to v1 (if needed).

Changed files

  • packages/nodes-base/credentials/DatabricksOAuth2Api.credentials.ts (modified, +8/-0)
RAW_BUFFERClick to expand / collapse

Bug Description

Summary

The databricksOAuth2Api credential extends oAuth2Api, which exposes a tokenExpiredStatusCode field for APIs that signal token expiry with a status other than 401. That field is marked doNotInherit: true, so it is stripped from every extending credential — including Databricks. As a result, if Databricks (or any endpoint fronted by it) ever returns 403 for an expired bearer token, n8n will not refresh the token and the request fails.

Environment

  • n8n: master (commit 345ce7f0dc)
  • Node: nodes-base/Databricks
  • Credential: databricksOAuth2Api (extends oAuth2Api, grantType: clientCredentials)

Current behavior

  1. Every Databricks operation calls this.helpers.httpRequestWithAuthentication.call(this, credentialType, { ... }) with no additionalCredentialOptions (e.g. packages/nodes-base/nodes/Databricks/actions/databricksSql/executeQuery.operation.ts:27).
  2. That routes to requestOAuth2 in packages/core/src/execution-engine/node-execution-context/utils/request-helper-functions.ts:1100-1109.
  3. resolveTokenExpiredStatusCode (request-helper-functions.ts:794-799) resolves the expected expiry code as: credentials.tokenExpiredStatusCode ?? oAuth2Options.tokenExpiredStatusCode ?? 401.
  4. For Databricks, both inputs are undefined, so the code is always 401.
  5. The status-code comparisons at request-helper-functions.ts:920 and :942 therefore miss 403 responses, retryWithNewToken is not invoked, and the error bubbles up to the user instead of triggering a fresh client_credentials token fetch.

Why the field is missing on databricksOAuth2Api

  • The base oAuth2Api credential defines tokenExpiredStatusCode with doNotInherit: true at packages/nodes-base/credentials/OAuth2Api.credentials.ts:197-205.
  • n8n's property-merge logic skips those props: if (property.doNotInherit) continue; at packages/workflow/src/node-helpers.ts:1688.
  • DatabricksOAuth2Api.credentials.ts does not redeclare the field, so it never reaches the UI or the decrypted credential object.

Expected behavior

When Databricks returns the configured "token expired" status code (default 401, but configurable for APIs that use 403), n8n should mint a new access token via client_credentials and retry the request once, as it does today for 401.

Proposed fixes (pick one)

  1. Preferred: Redeclare tokenExpiredStatusCode in DatabricksOAuth2Api.credentials.ts as a hidden field with a default of 401, so the value is present on the decrypted credential and any future change to the default requires only a credential edit.

    {
      displayName: 'Token Expired Status Code',
      name: 'tokenExpiredStatusCode',
      type: 'hidden',
      default: 401,
    }
  2. Pass { oauth2: { tokenExpiredStatusCode: <code> } } as the 4th argument to httpRequestWithAuthentication from each Databricks operation. Uglier, requires touching every call site.

  3. Remove doNotInherit: true from tokenExpiredStatusCode on the base oAuth2Api credential. Exposes the field to every OAuth2-extending credential in the codebase — broader blast radius, needs a wider audit.

Additional context

In practice Databricks' REST API returns 401 for expired bearer tokens, so most users will not hit this. The issue matters for:

  • Deployments sitting behind proxies/gateways that rewrite 401 → 403.
  • Databricks endpoints that genuinely respond 403 on auth failures (rare but possible for authorization-layer checks).
  • Consistency with other OAuth2 credentials that can configure this.

To Reproduce

Reproduction

  1. Configure a Databricks node with an OAuth2 credential.
  2. Point the node at an endpoint that returns 403 for an expired bearer token (either the real Databricks response, or a proxy/mock).
  3. Let the cached oauthTokenData expire (or invalidate it server-side).
  4. Execute the node.
  5. Observe: the workflow errors with a 403 instead of silently refreshing and retrying.

Expected behavior

OAUTH2 token auto-refreshes

Debug Info

Debug info

core

  • n8nVersion: 2.16.0
  • platform: docker (self-hosted)
  • nodeJsVersion: 24.14.1
  • nodeEnv: production
  • database: postgres
  • executionMode: scaling (single-main)
  • concurrency: -1
  • license: enterprise (production)
  • consumerId: 1028651a-d64b-438e-8d7b-506331acbbf5

storage

  • success: all
  • error: all
  • progress: false
  • manual: true
  • binaryMode: database

pruning

  • enabled: true
  • maxAge: 336 hours
  • maxCount: 10000 executions

client

  • userAgent: mozilla/5.0 (macintosh; intel mac os x 10_15_7) applewebkit/537.36 (khtml, like gecko) chrome/147.0.0.0 safari/537.36
  • isTouchDevice: false

Generated at: 2026-04-21T18:12:49.666Z

Operating System

docker

n8n Version

2.16.0

Node.js Version

24.14.1

Database

PostgreSQL

Execution mode

main (default)

Hosting

self hosted

extent analysis

TL;DR

The most likely fix is to redeclare tokenExpiredStatusCode in DatabricksOAuth2Api.credentials.ts as a hidden field with a default of 401.

Guidance

  • Redeclare tokenExpiredStatusCode in DatabricksOAuth2Api.credentials.ts to ensure the value is present on the decrypted credential.
  • Alternatively, pass { oauth2: { tokenExpiredStatusCode: <code> } } as the 4th argument to httpRequestWithAuthentication from each Databricks operation, but this approach requires touching every call site.
  • Removing doNotInherit: true from tokenExpiredStatusCode on the base oAuth2Api credential is another option, but it has a broader blast radius and needs a wider audit.
  • To verify the fix, configure a Databricks node with an OAuth2 credential, point it to an endpoint that returns 403 for an expired bearer token, and execute the node after letting the cached oauthTokenData expire.

Example

{
  displayName: 'Token Expired Status Code',
  name: 'tokenExpiredStatusCode',
  type: 'hidden',
  default: 401,
}

Notes

The proposed fixes assume that the issue is due to the missing tokenExpiredStatusCode field on the databricksOAuth2Api credential. The choice of fix depends on the desired approach and the potential impact on the codebase.

Recommendation

Apply the workaround by redeclaring tokenExpiredStatusCode in DatabricksOAuth2Api.credentials.ts, as it is the preferred solution with minimal impact on the codebase.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

OAUTH2 token auto-refreshes

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

n8n - ✅(Solved) Fix Databricks OAuth2: token refresh is hardcoded to 401, cannot handle 403 [1 pull requests, 1 comments, 2 participants]