usebruno/bruno · error · Error
Failed to fetch PAC (${err.response.status})
Error message
Failed to fetch PAC (${err.response.status}) What it means
Thrown by downloadPac when axios.get(pacSource) completes with an HTTP response (err.response is defined) whose status is not 2xx. It distinguishes an HTTP-level failure of the PAC host from a transport-level failure (DNS, timeout, connection refused) which is re-thrown unchanged. The status code is embedded so the caller can decide whether to retry, fall back, or surface to the user.
Source
Thrown at packages/bruno-requests/src/utils/pac-resolver.ts:62
proxy: false,
responseType: 'text',
maxRedirects: 3
};
if (pacSource.startsWith('https://')) {
const agentOpts: AgentOptions = {
ca: tlsOptions.ca,
rejectUnauthorized: tlsOptions.rejectUnauthorized,
minVersion: tlsOptions.minVersion as AgentOptions['minVersion']
};
config.httpsAgent = new https.Agent(agentOpts);
}
try {
const response = await axios.get(pacSource, config);
return response.data;
} catch (err: any) {
if (err.response) throw new Error(`Failed to fetch PAC (${err.response.status})`);
throw err;
}
}
export type GetPacResolverParams = {
pacSource: string;
httpsAgentRequestFields?: TlsOptions;
opts?: { cacheTtlMs?: number; timeoutMs?: number };
};
export async function getPacResolver({ pacSource, httpsAgentRequestFields = {}, opts = {} }: GetPacResolverParams): Promise<PacWrapper> {
if (!pacSource) throw new Error('pacSource must be provided');
const cacheTtlMs = opts.cacheTtlMs ?? 5 * 60 * 1000;
let key: string;
if (pacSource.startsWith('https://')) {
const caRaw = httpsAgentRequestFields.ca;
const caHash = caRawView on GitHub (pinned to 9bdd81c7bd)
Solutions
- Verify the PAC URL by opening it in a browser or with curl -i; expect a 200 with JavaScript content.
- If 401/403: provide the required auth (the library disables axios proxy with proxy:false, so authenticating to an upstream proxy is not supported — host the PAC on an unauthenticated endpoint instead).
- If 502/503: treat as transient and retry with backoff, or fall back to direct connection and warn the user.
- If 404: correct the configured pac_url in the collection/system proxy settings.
Example fix
// before
const pac = await getPacResolver({ pacSource: 'https://corp/proxy.pac' });
// after
let pac;
try {
pac = await getPacResolver({ pacSource: 'https://corp/proxy.pac' });
} catch (e) {
if (/Failed to fetch PAC \(5\d{2}\)/.test(e.message)) {
// gateway hiccup — retry once, then degrade to direct
pac = await getPacResolver({ pacSource: 'https://corp/proxy.pac' }).catch(() => null);
} else throw e;
} Defensive patterns
Strategy: retry
Validate before calling
async function pacReachable(url, { timeoutMs = 3000 } = {}) {
try {
const res = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(timeoutMs) });
if (!res.ok) return { ok: false, status: res.status };
const text = await res.text();
return { ok: /function\s+FindProxyForURL/i.test(text), status: res.status, text };
} catch (e) { return { ok: false, status: 0, error: e }; }
}
const probe = await pacReachable(pacSource);
if (!probe.ok) throw new Error(`PAC not usable (${probe.status})`); Type guard
function isPacFetchError(e) {
return e instanceof Error && /^Failed to fetch PAC \(\d+\)$/.test(e.message);
}
function pacStatusFromError(e) {
const m = e?.message?.match(/Failed to fetch PAC \((\d+)\)/);
return m ? Number(m[1]) : null;
} Try / catch
let pac;
try {
pac = await getPacResolver({ pacSource, httpsAgentRequestFields, opts });
} catch (e) {
const status = pacStatusFromError(e);
if (status && status >= 500 && attemptsLeft > 0) {
await delay(500); return getPacResolverWithRetry(...); // retry 5xx once
}
if (status === 404 || status === 401 || status === 403) {
// non-transient — degrade to direct and warn
pac = null;
} else throw e;
} Prevention
- Host the PAC on an unauthenticated, highly-available endpoint (the fetch disables upstream proxying).
- Validate the PAC URL returns FindProxyForURL at config time, not just at first request.
- Cache the resolved PAC aggressively (the library caches for 5 min) to absorb short outages.
- Distinguish 5xx (retry/fallback) from 4xx (correct the URL).
When it happens
Trigger: getPacResolver/downloadPac is called with a pacSource pointing to a PAC URL that returns 401/403 (auth required), 404 (wrong URL), 500/502/503 (proxy host down or misconfigured gateway), or any other non-2xx. The catch only fires when err.response exists; pure network errors propagate as the original axios error.
Common situations: Corporate PAC URL changed and the configured value is now stale (404); PAC host sits behind SSO/auth that rejects unauthenticated requests (401/403); the PAC-serving reverse proxy is temporarily down (502/503); typo in the URL; PAC endpoint moved off the default port.
Related errors
- Invalid system https_proxy
- Invalid system http_proxy "${http_proxy}": ${error.message}
- Invalid system https_proxy "${https_proxy}": ${error.message
- pacSource must be provided
- Could not reach the mock server
AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13).
Data as JSON: /api/errors/08e04d64de106678.
Report an issue: GitHub.