redis/node-redis · error · Error
Invalid token response
Error message
Invalid token response
What it means
Thrown by MSALIdentityProvider.requestToken() when the MSAL AuthenticationResult lacks accessToken or expiresOn (msal-identity-provider.ts:16). The identity provider feeds whatever the underlying MSAL/DefaultAzureCredential acquireToken returned into the token manager; a result with no usable accessToken or no expiry cannot be scheduled for refresh and is rejected as malformed.
Source
Thrown at packages/entraid/lib/msal-identity-provider.ts:17
import {
AuthenticationResult
} from '@azure/msal-node';
import { IdentityProvider, TokenResponse } from '@redis/client/dist/lib/authx';
export class MSALIdentityProvider implements IdentityProvider<AuthenticationResult> {
private readonly getToken: () => Promise<AuthenticationResult>;
constructor(getToken: () => Promise<AuthenticationResult>) {
this.getToken = getToken;
}
async requestToken(): Promise<TokenResponse<AuthenticationResult>> {
const result = await this.getToken();
if (!result?.accessToken || !result?.expiresOn) {
throw new Error('Invalid token response');
}
return {
token: result,
ttlMs: result.expiresOn.getTime() - Date.now()
};
}
}
View on GitHub (pinned to bb5beb5657)
Solutions
- Verify scopes: client-credentials flows should use ['https://redis.azure.com/.default'].
- Check the service principal: client secret not expired, certificate valid and uploaded to the app registration.
- Confirm clientId/tenantId/authority match the app registration.
- Enable MSAL logging (loggerOptions.logLevel) to capture the underlying acquireToken result/error.
Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity-check the MSAL result shape your provider returns before wrapping it.
function hasUsableToken(r: any): boolean {
return !!r && typeof r.accessToken === 'string' && r.accessToken.length > 0 &&
(r.expiresOn instanceof Date || typeof r.expiresOn?.getTime === 'function');
} Type guard
function isAuthenticationResult(v: unknown): v is { accessToken: string; expiresOn: Date } {
return typeof v === 'object' && v !== null &&
typeof (v as any).accessToken === 'string' && (v as any).accessToken.length > 0 &&
(v as any).expiresOn instanceof Date;
} Try / catch
try {
const provider = EntraIdCredentialsProviderFactory.createForClientCredentials(params);
} catch (e) {
if (e instanceof Error && /Invalid token response/.test(e.message)) {
// verify scopes, client secret/cert, and tenant; enable MSAL logging
}
throw e;
} Prevention
- Use the correct scope for your flow (client credentials: 'https://redis.azure.com/.default').
- Keep client secrets/certificates current and valid.
- Enable MSAL loggerOptions to surface the underlying acquireToken failure.
- Validate clientId/tenantId/authority against the app registration.
When it happens
Trigger: MSAL acquireToken* resolves to a result where accessToken is null/empty or expiresOn is missing — e.g. wrong scopes requested, client secret/certificate misconfigured, the service principal disabled, or an unusual MSAL flow returning a partial result. Also triggered upstream by the `.then(x => x === null ? Promise.reject('Token is null') : x)` guards being bypassed by a non-null but empty result.
Common situations: Wrong scope string (e.g. not 'https://redis.azure.com/.default'); expired/revoked client secret; certificate thumbprint/privateKey mismatch; tenant misconfiguration returning an opaque token; MSAL cache returning a stale partial entry.
Related errors
- Invalid authority configuration
- MSAL_CLIENT_ID and MSAL_TENANT_ID environment variables must
- MSAL_CLIENT_ID and MSAL_TENANT_ID environment variables must
- SESSION_SECRET environment variable must be set
- SESSION_SECRET environment variable must be set
AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03).
Data as JSON: /data/errors/5a550ba95e753416.json.
Report an issue: GitHub.