oven-sh/bun · critical · Error

[azure] Auth failed: ${response.status} ${await response.tex

Error message

[azure] Auth failed: ${response.status} ${await response.text()}

What it means

OAuth2 client-credentials token request to login.microsoftonline.com returned non-2xx; the status and body are included in the message. This is the entry point for every Azure REST call in the module (the token is cached until _tokenExpiry), so nothing Azure-related works until it is fixed.

Source

Thrown at scripts/azure.mjs:72

async function getAccessToken() {
  if (_accessToken && Date.now() < _tokenExpiry - 300_000) {
    return _accessToken;
  }

  const { tenantId, clientId, clientSecret } = config();
  const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_id: clientId,
      client_secret: clientSecret,
      scope: "https://management.azure.com/.default",
    }),
  });

  if (!response.ok) {
    throw new Error(`[azure] Auth failed: ${response.status} ${await response.text()}`);
  }

  const data = await response.json();
  _accessToken = data.access_token;
  _tokenExpiry = Date.now() + data.expires_in * 1000;
  return _accessToken;
}

// ============================================================================
// REST Client
// ============================================================================

/**
 * @param {"GET"|"PUT"|"POST"|"PATCH"|"DELETE"} method
 * @param {string} path - Relative path under management.azure.com, or absolute URL
 * @param {object} [body]
 * @param {string} [apiVersion]
 */

View on GitHub (pinned to 8c5296ac45)

Solutions

  1. Read the response body in the error — AADSTS codes state exactly what is wrong
  2. Verify the triple: az login --service-principal -u clientId -p clientSecret --tenant tenantId
  3. Rotate the client secret in Azure and update the stored CI secret if it expired
  4. Confirm tenantId is the directory (tenant) GUID, not the subscription GUID

Example fix

# before
# stale secret in the store -> 401 invalid_client

# after
$ az ad sp credential reset --id $AZURE_CLIENT_ID  # rotate
# store the new secret, then verify:
$ az login --service-principal -u $AZURE_CLIENT_ID -p $AZURE_CLIENT_SECRET --tenant $AZURE_TENANT_ID
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await getAccessToken();
} catch (err) {
  if (/Auth failed/.test(err.message)) {
    console.error('Azure credentials rejected (check tenant/client id/secret expiry):', err.message);
    process.exit(1); // config problem: do not retry
  }
  throw err;
}

Prevention

When it happens

Trigger: Wrong AZURE_TENANT_ID or AZURE_CLIENT_ID; expired/rotated AZURE_CLIENT_SECRET (AADSTS7000215 / invalid_client); secret with trailing whitespace or not URL-encoded properly in the form body; the service principal was deleted.

Common situations: Client secret expired (Azure secrets max out at 2 years) and CI still holds the old value; values copied with quotes or spaces; tenant id swapped for subscription id.

Related errors


AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16). Data as JSON: /api/errors/5ab94ff37b241ae5. Report an issue: GitHub.