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
- Read the response body in the error — AADSTS codes state exactly what is wrong
- Verify the triple: az login --service-principal -u clientId -p clientSecret --tenant tenantId
- Rotate the client secret in Azure and update the stored CI secret if it expired
- 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
- Rotate AZURE_CLIENT_SECRET on a schedule before the 2-year expiry
- Validate the credential triple with az login --service-principal before CI runs
- Parse AADSTS codes from the response body for precise failures
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
- Azure secret not found: ${name}
- Azure auth failed: ${response.status}
- Buildkite token not found: set BUILDKITE_AGENT_TOKEN or gran
- Failed to fetch Sentry event: ${response.statusText}
- Failed to fetch Sentry issue: ${issueResponse.statusText}
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/5ab94ff37b241ae5.
Report an issue: GitHub.