Skip to content

Commit 2a9fad7

Browse files
committed
fix(proxy): degrade to default dispatcher when proxy agent init fails instead of crashing startup
1 parent 5d43a71 commit 2a9fad7

2 files changed

Lines changed: 44 additions & 2 deletions

File tree

src/lib/proxy.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,29 @@ describe('maybeInstallProxyAgent', () => {
3131
expect(maybeInstallProxyAgent({ env: { HTTPS_PROXY: '' }, install })).toBe(false);
3232
expect(install).not.toHaveBeenCalled();
3333
});
34+
35+
it('falls back (returns false, warns, never throws) when installing the agent fails', () => {
36+
// A malformed/unsupported proxy value makes the agent throw at startup; the
37+
// CLI must degrade to a proxy-less dispatcher, not crash every command.
38+
const errs: string[] = [];
39+
const installed = maybeInstallProxyAgent({
40+
env: { HTTPS_PROXY: 'http://proxy.corp.example.com:8080' },
41+
install: () => {
42+
throw new Error('unsupported proxy scheme');
43+
},
44+
stderr: line => errs.push(line),
45+
});
46+
expect(installed).toBe(false);
47+
expect(errs.join('\n')).toContain('ignoring proxy environment');
48+
});
49+
50+
it('still installs when NO_PROXY is set (exemptions are applied per request by undici)', () => {
51+
const install = vi.fn();
52+
const installed = maybeInstallProxyAgent({
53+
env: { HTTPS_PROXY: 'http://proxy.corp.example.com:8080', NO_PROXY: 'localhost,127.0.0.1' },
54+
install,
55+
});
56+
expect(installed).toBe(true);
57+
expect(install).toHaveBeenCalledTimes(1);
58+
});
3459
});

src/lib/proxy.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export interface ProxyDeps {
2020
env?: NodeJS.ProcessEnv;
2121
/** Dispatcher installer. Defaults to undici's setGlobalDispatcher. */
2222
install?: (agent: Dispatcher) => void;
23+
/** Warning sink. Defaults to `process.stderr`. */
24+
stderr?: (line: string) => void;
2325
}
2426

2527
/**
@@ -36,6 +38,21 @@ export function maybeInstallProxyAgent(deps: ProxyDeps = {}): boolean {
3638
const install = deps.install ?? setGlobalDispatcher;
3739
// EnvHttpProxyAgent reads HTTPS_PROXY/HTTP_PROXY/NO_PROXY itself, per
3840
// request, so NO_PROXY exemptions apply without extra plumbing here.
39-
install(new EnvHttpProxyAgent());
40-
return true;
41+
//
42+
// A malformed or unsupported proxy value (e.g. `socks5://...`) makes the
43+
// agent throw. Because this runs at startup, an unguarded throw would abort
44+
// every command before the CLI's own error handling — so fall back to the
45+
// default (proxy-less) dispatcher and warn instead of crashing.
46+
try {
47+
install(new EnvHttpProxyAgent());
48+
return true;
49+
} catch (error) {
50+
const stderr = deps.stderr ?? ((line: string) => process.stderr.write(`${line}\n`));
51+
stderr(
52+
`warning: ignoring proxy environment (could not initialize proxy agent): ${
53+
error instanceof Error ? error.message : String(error)
54+
}`,
55+
);
56+
return false;
57+
}
4158
}

0 commit comments

Comments
 (0)