{"id":"5a550ba95e753416","repo":"redis/node-redis","slug":"invalid-token-response","errorCode":null,"errorMessage":"Invalid token response","messagePattern":"Invalid token response","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/entraid/lib/msal-identity-provider.ts","lineNumber":17,"sourceCode":"import {\n  AuthenticationResult\n} from '@azure/msal-node';\nimport { IdentityProvider, TokenResponse } from '@redis/client/dist/lib/authx';\n\nexport class MSALIdentityProvider implements IdentityProvider<AuthenticationResult> {\n  private readonly getToken: () => Promise<AuthenticationResult>;\n\n  constructor(getToken: () => Promise<AuthenticationResult>) {\n    this.getToken = getToken;\n  }\n\n  async requestToken(): Promise<TokenResponse<AuthenticationResult>> {\n    const result = await this.getToken();\n\n    if (!result?.accessToken || !result?.expiresOn) {\n      throw new Error('Invalid token response');\n    }\n    return {\n      token: result,\n      ttlMs: result.expiresOn.getTime() - Date.now()\n    };\n  }\n\n}\n","sourceCodeStart":1,"sourceCodeEnd":26,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/entraid/lib/msal-identity-provider.ts#L1-L26","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Sanity-check the MSAL result shape your provider returns before wrapping it.\nfunction hasUsableToken(r: any): boolean {\n  return !!r && typeof r.accessToken === 'string' && r.accessToken.length > 0 &&\n    (r.expiresOn instanceof Date || typeof r.expiresOn?.getTime === 'function');\n}","typeGuard":"function isAuthenticationResult(v: unknown): v is { accessToken: string; expiresOn: Date } {\n  return typeof v === 'object' && v !== null &&\n    typeof (v as any).accessToken === 'string' && (v as any).accessToken.length > 0 &&\n    (v as any).expiresOn instanceof Date;\n}","tryCatchPattern":"try {\n  const provider = EntraIdCredentialsProviderFactory.createForClientCredentials(params);\n} catch (e) {\n  if (e instanceof Error && /Invalid token response/.test(e.message)) {\n    // verify scopes, client secret/cert, and tenant; enable MSAL logging\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["entraid","authentication","azure","token"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}