Windows tool shell contains both Path and PATH, breaking Start-Process with redirected stdio
Summary
Codex Desktop on Windows launches tool shells with both Path and PATH in the process environment. Windows environment variable lookup is case-insensitive, and this duplicate breaks PowerShell/.NET APIs that copy the environment into a case-insensitive dictionary.
Environment
- OS: Windows
- Codex Desktop package observed:
OpenAI.Codex_26.608.1337.0_x64__2p2nqsd0c76g0 - Workspace shell: Windows PowerShell
Reproduction
In a normal PowerShell session outside Codex:
[Environment]::GetEnvironmentVariables('Process').Keys | Where-Object { $_ -ieq 'PATH' }
Observed outside Codex:
Path
Inside a Codex tool shell, run the same command:
[Environment]::GetEnvironmentVariables('Process').Keys | Where-Object { $_ -ieq 'PATH' }
Observed inside Codex:
Path
PATH
The two values are identical except for Codex-added path entries.
Then attempt a normal background launch with redirected stdio:
$out = Join-Path (Get-Location) 'server.out.log'
$err = Join-Path (Get-Location) 'server.err.log'
Start-Process -FilePath node -ArgumentList 'server.mjs' -WorkingDirectory (Get-Location) -WindowStyle Hidden -RedirectStandardOutput $out -RedirectStandardError $err -PassThru
Observed error:
Start-Process : Item has already been added. Key in dictionary: 'Path' Key being added: 'PATH'
Expected behavior
On Windows, Codex should normalize environment variable keys case-insensitively before launching tool shells/commands. It should preserve the canonical Windows Path key and update that key when prepending Codex paths, rather than creating a separate PATH key.
Why this matters
This prevents Codex from using normal PowerShell Start-Process patterns for local dev servers with redirected stdout/stderr. Foreground node server.mjs works, but normal detached process launching from Codex fails because of the duplicate environment key.
Suggested fix
Before spawning Windows shell/tool commands, normalize env keys case-insensitively. For the path variable specifically, keep one canonical key:
function normalizeWindowsEnv(env: Record<string, string>) {
if (process.platform !== 'win32') return env;
const output: Record<string, string> = {};
const seen = new Map<string, string>();
for (const [key, value] of Object.entries(env)) {
const lower = key.toLowerCase();
if (lower === 'path') {
const canonical = seen.get('path') ?? 'Path';
output[canonical] = value;
seen.set('path', canonical);
continue;
}
if (!seen.has(lower)) {
seen.set(lower, key);
output[key] = value;
}
}
return output;
}
Then prepend Codex-specific path entries to the retained Path value instead of adding PATH.