{"record":{"id":"2a15879a64b8eb16","repo":"mastra-ai/mastra","slug":"authfailureerror-authfailurestatus-error","errorCode":null,"errorMessage":"AuthFailureError(authFailureStatus, error)","messagePattern":"AuthFailureError\\(authFailureStatus, error\\)","errorType":"exception","errorClass":"AuthFailureError","httpStatus":null,"severity":"error","filePath":"observability/mastra/src/exporters/auth-failure-cooldown.ts","lineNumber":49,"sourceCode":"  maxRetries: number,\n  callerShouldRetry?: (response: Response) => boolean,\n): Promise<Response> {\n  let authFailureStatus: number | undefined;\n\n  try {\n    return await fetchWithRetry(url, options, maxRetries, {\n      shouldRetryResponse: response => {\n        if (isAuthFailureStatus(response.status)) {\n          authFailureStatus = response.status;\n          return false;\n        }\n\n        return callerShouldRetry?.(response) ?? true;\n      },\n    });\n  } catch (error) {\n    if (authFailureStatus !== undefined) {\n      throw new AuthFailureError(authFailureStatus, error);\n    }\n\n    throw error;\n  }\n}\n\nexport class AuthFailureCooldown {\n  private failureCount = 0;\n  private cooldownUntilMs = 0;\n  private droppedEventsDuringCooldown = 0;\n\n  constructor(\n    private readonly exporterName: string,\n    private readonly getLogger: () => IMastraLogger,\n  ) {}\n\n  private shouldDropEvents(): boolean {\n    return Date.now() < this.cooldownUntilMs;","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/observability/mastra/src/exporters/auth-failure-cooldown.ts#L31-L67","documentation":"AuthFailureError is thrown by fetchWithAuthFailureHandling when the exporter's HTTP request (via fetchWithRetry inside batchUpload) received a 401 or 403 response. On the first auth-failing response, retries are suppressed (shouldRetryResponse returns false) and the original error is wrapped as AuthFailureError carrying the HTTP status and the underlying error as `cause`. This signals the exporter's credentials are invalid or insufficient, triggering the AuthFailureCooldown backoff instead of pointless retries.","triggerScenarios":"batchUpload (traces/logs export) hitting the observability backend which responds 401 Unauthorized or 403 Forbidden: expired/rotated API keys, missing Authorization header, revoked service-account tokens, or insufficient permissions for the endpoint.","commonSituations":"Expired API key in the observability provider dashboard; credentials rotated in the environment but the process still runs with old values; a token scoped to a different project/region; clock drift invalidating short-lived JWTs; exporting to an endpoint the API key has no access to.","solutions":["Check the `status` property (401 vs 403) and the `cause` to determine whether the key is unrecognized (401) or lacks permission (403).","Regenerate or rotate the exporter's API key/token and update the environment variable or exporter config, then restart the process.","Verify the Authorization header/credentials are actually attached to the exporter request and are scoped to the correct project/region.","Wait out the AuthFailureCooldown backoff (60s doubling to a 15-minute cap) after fixing credentials — note any events dropped during cooldown in the reset() return value.","Test the key with a direct curl to the export endpoint to confirm auth works before re-enabling exports."],"exampleFix":"// before\nnew Exporter({ apiKey: process.env.STALE_OBSERVABILITY_KEY })\n// after\nnew Exporter({ apiKey: process.env.NEW_OBSERVABILITY_KEY }) // rotated key, verified with curl -H \"Authorization: Bearer $NEW_OBSERVABILITY_KEY\"","handlingStrategy":"try-catch","validationCode":"// preflight: verify credentials before enabling the exporter\nconst res = await fetch(exportEndpoint, { headers: { Authorization: `Bearer ${apiKey}` }, method: 'HEAD' });\nif (res.status === 401 || res.status === 403) {\n  throw new Error(`Observability credentials rejected with ${res.status}; rotate the key before starting exports.`);\n}","typeGuard":"function isAuthFailureError(e: unknown): e is AuthFailureError {\n  return e instanceof AuthFailureError;\n}","tryCatchPattern":"import { isAuthFailureError } from '@mastra/observability/exporters/auth-failure-cooldown';\ntry {\n  await exporter.batchUpload(signals);\n} catch (err) {\n  if (isAuthFailureError(err)) {\n    logger.error(`Exporter auth failed (HTTP ${err.status}). Rotate credentials; cooldown active.`, { cause: err.cause });\n    // do not retry immediately — AuthFailureCooldown backs off 60s -> 15min\n  } else {\n    throw err;\n  }\n}","preventionTips":["Monitor key expiry and rotate observability API keys on a schedule before they lapse.","Add a startup preflight that hits the export endpoint to verify credentials early.","Ensure the Authorization header/credential env var is actually injected in the deploy environment.","Use 401 vs 403 from err.status to distinguish bad-key vs insufficient-permissions when triaging.","After fixing credentials, call reset() on AuthFailureCooldown and log how many events were dropped during cooldown."],"tags":["authentication","http","network","observability","exporter"],"backgroundTag":"http-401-unauthorized","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}