garrytan/gstack · error · ProxyConfigError
proxy creds set in both env (BROWSE_PROXY_USER) and URL — pi
Error message
proxy creds set in both env (BROWSE_PROXY_USER) and URL — pick one source
What it means
Thrown by parseProxyConfig when credentials appear in BOTH the URL (user:pass@host) AND the environment (BROWSE_PROXY_USER / BROWSE_PROXY_PASS). This is the D9 'codex correction': the parser refuses to guess which source wins, because a silent override is a debugging trap — a stale env var from a prior session could win over a fresh --proxy URL and the user would have no idea why auth failed.
Source
Thrown at browse/src/proxy-config.ts:91
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
throw new ProxyConfigError(
'expected scheme://[user:pass@]host:port',
`invalid proxy URL — bad port`,
);
}
const urlHasUser = !!url.username;
const urlHasPass = !!url.password;
const envHasUser = !!opts.envUser;
const envHasPass = !!opts.envPass;
const urlHasCreds = urlHasUser || urlHasPass;
const envHasCreds = envHasUser || envHasPass;
// D9 (codex correction): refuse on mixed sources. Silent override is a
// debugging trap — when a stale BROWSE_PROXY_USER from a prior session
// wins over a fresh --proxy URL, the user can't tell why.
if (urlHasCreds && envHasCreds) {
throw new ProxyConfigError(
'unset BROWSE_PROXY_USER/PASS or remove user:pass@ from --proxy',
`proxy creds set in both env (BROWSE_PROXY_USER) and URL — pick one source`,
);
}
let userId: string | undefined;
let password: string | undefined;
if (urlHasCreds) {
userId = decodeURIComponent(url.username);
password = url.password ? decodeURIComponent(url.password) : undefined;
} else if (envHasCreds) {
userId = opts.envUser;
password = opts.envPass;
}
return {
scheme: scheme as 'socks5' | 'http' | 'https',
host: url.hostname,View on GitHub (pinned to 94993f7401)
Solutions
- Unset the env vars: `unset BROWSE_PROXY_USER BROWSE_PROXY_PASS`
- OR remove user:pass@ from the --proxy URL and rely on env only
- Pick one credential source and document it in your shell rc / CI config
- Audit wrappers and dotfiles for stray BROWSE_PROXY_* exports
Example fix
# before: creds in both places export BROWSE_PROXY_USER=alice browse --proxy bob:pw@host:1080 ... # throws # after: pick one source unset BROWSE_PROXY_USER browse --proxy bob:pw@host:1080 ...
Defensive patterns
Strategy: validation
Validate before calling
function credsNotInBothPlaces(proxyUrl: string, envUser?: string, envPass?: string): boolean {
let urlHasCreds = false;
try {
const u = new URL(proxyUrl);
urlHasCreds = !!u.username || !!u.password;
} catch { /* invalid URL handled elsewhere */ }
const envHasCreds = !!envUser || !!envPass;
return !(urlHasCreds && envHasCreds);
}
if (!credsNotInBothPlaces(proxyUrl, envUser, envPass)) {
throw new Error('Set proxy creds in EITHER the URL OR the env, not both');
} Type guard
function hasUrlCreds(s: string): boolean {
try { const u = new URL(s); return !!u.username || !!u.password; } catch { return false; }
} Try / catch
try {
parseProxyConfig(opts);
} catch (e) {
if (e instanceof ProxyConfigError && /both env/.test(e.message)) {
console.error(`${e.message}. ${e.hint}`);
// unset env and retry with URL creds
delete process.env.BROWSE_PROXY_USER;
delete process.env.BROWSE_PROXY_PASS;
parseProxyConfig({ ...opts, envUser: undefined, envPass: undefined });
} else throw e;
} Prevention
- Pick ONE credential source (URL or env) and document it in your setup
- Audit shell rc files and CI secrets for stray BROWSE_PROXY_USER/PASS exports
- When switching auth strategies, unset the old source explicitly
- Validate for dual-source conflict in your config loader before calling parseProxyConfig
When it happens
Trigger: Setting --proxy user:pass@host AND exporting BROWSE_PROXY_USER (or BROWSE_PROXY_PASS) in the same shell, then running the CLI. Both urlHasCreds and envHasCreds are true, so the parser throws.
Common situations: Stale env vars from a prior session combined with a fresh --proxy flag; CI that sets BROWSE_PROXY_USER globally and also embeds creds in the URL per-job; wrapper script that injects creds from both sources; switching auth strategies without clearing the old one.
Related errors
- invalid proxy URL — could not parse
- unsupported proxy scheme '${scheme}'
- invalid proxy URL — missing host
- invalid proxy URL — bad port
- invalid_allowlist
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/4ad0cb454476c049.
Report an issue: GitHub.