n8n-io/n8n · error · Error

OAuth access token expired and no refresh token is available

Error message

OAuth access token expired and no refresh token is available. Please reconnect the credentials.

What it means

Generic Error thrown by ClientOAuth2Token.refresh when this.refreshToken is falsy at the start of the refresh flow (RFC 6749 §6). Without a refresh token the client cannot obtain a new access token, so the user must re-authenticate. The message is user-facing and instructs reconnection.

Source

Thrown at packages/@n8n/client-oauth2/src/client-oauth2-token.ts:79

			// Attempt to avoid storing the url in proxies, since the access token
			// is exposed in the query parameters.
			requestObject.headers.Pragma = 'no-store';
			requestObject.headers['Cache-Control'] = 'no-store';
		}

		return requestObject;
	}

	/**
	 * Refresh a user access token with the refresh token.
	 * As in RFC 6749 Section 6: https://www.rfc-editor.org/rfc/rfc6749.html#section-6
	 * Supports PKCE flows (RFC 7636) for public clients without client secret
	 */
	async refresh(opts?: ClientOAuth2Options): Promise<ClientOAuth2Token> {
		const options = { ...this.client.options, ...opts };

		if (!this.refreshToken) {
			throw new Error(
				'OAuth access token expired and no refresh token is available. Please reconnect the credentials.',
			);
		}

		const { clientId, clientSecret } = options;
		const headers = { ...DEFAULT_HEADERS };
		const body: Record<string, string> = {
			refresh_token: this.refreshToken,
			grant_type: 'refresh_token',
			...(options.resource ? { resource: options.resource } : {}),
		};

		if (options.clientCredentialType === 'certificate') {
			expects(options, 'clientCertificate');
			body.client_id = clientId;
			body.client_assertion_type = CLIENT_ASSERTION_TYPE;
			body.client_assertion = buildClientAssertion({
				clientId,

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Reconnect the OAuth2 credential in n8n so a fresh authorization code (with refresh scope) is exchanged.
  2. Ensure the OAuth2 credential config requests offline_access (Microsoft/Azure) or the provider's refresh scope.
  3. Switch to an authorization grant that issues refresh tokens (authorization_code with offline_access).
  4. If using client_credentials, do not rely on refresh — fetch a new token directly.
Defensive patterns

Strategy: validation

Validate before calling

// Before refreshing, check whether a refresh token exists
if (!credential.oauthRefreshToken) {
  throw new Error('Reconnect the credential — no refresh token available');
}

Type guard

const hasRefreshToken = (token: { refreshToken?: string }): boolean => !!token.refreshToken;

Try / catch

try {
  return await oauthToken.refresh();
} catch (e) {
  if (e instanceof Error && /no refresh token is available/.test(e.message)) {
    // mark credential as needing reconnection in the UI
  }
  throw e;
}

Prevention

When it happens

Trigger: A credential's access token has expired (token.expired() true), the n8n runtime calls token.refresh(), but the stored credential has no refresh_token. Common when the original grant did not include one.

Common situations: OAuth2 flow was configured without the offline_access / refresh scope; the provider's grant type does not issue refresh tokens (e.g. client_credentials, some implicit flows, resource owner password with certain providers); the refresh token was already consumed and rotated out; the credential was imported without its refresh token.

Understand the failure class

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/d2936362de877c29. Report an issue: GitHub.