What happened?
While investigating a Windows standalone update failure, I found that the update action launches Windows PowerShell (powershell.exe) even when Codex itself is started from PowerShell 7 (pwsh). The child powershell.exe process can inherit PowerShell 7 entries in PSModulePath, which breaks module discovery in Windows PowerShell 5.1 and makes Get-FileHash unavailable.
The resulting installer failure looks like this:
Get-FileHash : The term 'Get-FileHash' is not recognized as the name of a cmdlet, function, script file, or operable program.
This does not appear to be caused by irm ... | iex corrupting the installer script text. I verified that the downloaded script text and piped script text are identical and parse successfully.
Environment
OS: Windows 10 22H2, 10.0.19045
Codex: codex-cli 0.138.0
Parent shell: pwsh 7.6.2
Child shell used by standalone update: powershell.exe 5.1.19041.6456
Codex executable: C:\Users\Administrator\AppData\Local\Programs\OpenAI\Codex\bin\codex.exe
Current main still has the Windows standalone update action hard-coded as:
UpdateAction::StandaloneWindows => (
"powershell",
&[
"-ExecutionPolicy",
"Bypass",
"-c",
"$env:CODEX_NON_INTERACTIVE=1; irm https://chatgpt.com/codex/install.ps1 | iex",
],
),
Observed on main commit:
08cb633c06a27d25872d0132fbd9c749556d7653
Minimal reproduction
This does not execute the Codex installer. It only simulates the relevant process-launch shape and checks whether Windows PowerShell can resolve and run Get-FileHash.
Run this from pwsh:
node - <<'NODE'
const { spawnSync } = require('child_process');
const psProbe = String.raw`
$ErrorActionPreference = 'Continue'
"PSVersion=$($PSVersionTable.PSVersion) Edition=$($PSVersionTable.PSEdition)"
"PSModulePath=$env:PSModulePath"
$cmd = Get-Command Get-FileHash -ErrorAction SilentlyContinue
"Get-FileHash found=$([bool]$cmd) type=$($cmd.CommandType) source=$($cmd.Source)"
try {
'Get-FileHash -LiteralPath $PSHOME\\powershell.exe -Algorithm SHA256 | Select-Object -First 1 Algorithm,Hash' | Invoke-Expression | Format-List
"InvokeExpressionResult=OK"
} catch {
"InvokeExpressionResult=FAIL"
"Error=$($_.Exception.Message)"
}
`;
const enc = Buffer.from(psProbe, 'utf16le').toString('base64');
const cleanWindowsPSModulePath = [
'C:\\Users\\Administrator\\Documents\\WindowsPowerShell\\Modules',
'C:\\Program Files\\WindowsPowerShell\\Modules',
'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules',
].join(';');
const cases = [
['A. inherited env, like native child process', { ...process.env }],
['B. PSModulePath removed, let Windows PowerShell rebuild', (() => { const e = { ...process.env }; delete e.PSModulePath; delete e.PSMODULEPATH; return e; })()],
['C. clean WindowsPowerShell-only PSModulePath', { ...process.env, PSModulePath: cleanWindowsPSModulePath }],
];
for (const [name, env] of cases) {
const r = spawnSync('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', enc], {
encoding: 'utf8', timeout: 30000, env
});
console.log('\n==== ' + name + ' ====');
console.log('exit=' + r.status);
console.log(r.stdout.trim());
const stderr = r.stderr.replace(/#< CLIXML[\s\S]*/,'<CLIXML progress omitted>').trim();
if (stderr) console.log('stderr=' + stderr);
}
NODE
Actual output
With the inherited environment, Windows PowerShell 5.1 sees PowerShell 7 module paths first and cannot resolve Get-FileHash:
==== A. inherited env, like native child process ====
exit=0
PSVersion=5.1.19041.6456 Edition=Desktop
PSModulePath=C:\Users\Administrator\Documents\PowerShell\Modules;C:\Program Files\PowerShell\Modules;c:\program files\powershell\7\Modules;C:\Program Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules
Get-FileHash found=False type= source=
InvokeExpressionResult=FAIL
Error=The term 'Get-FileHash' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
If PSModulePath is removed before spawning powershell.exe, Windows PowerShell rebuilds a WindowsPowerShell-only module path and Get-FileHash works:
==== B. PSModulePath removed, let Windows PowerShell rebuild ====
exit=0
PSVersion=5.1.19041.6456 Edition=Desktop
PSModulePath=C:\Users\Administrator\Documents\WindowsPowerShell\Modules;C:\Program Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules
Get-FileHash found=True type=Function source=Microsoft.PowerShell.Utility
Algorithm : SHA256
Hash : 9785001B0DCF755EDDB8AF294A373C0B87B2498660F724E76C4D53F9C217C7A3
InvokeExpressionResult=OK
Codex-shaped safe reproduction
This mirrors the update action shape (powershell -ExecutionPolicy Bypass -c "... | iex") without running the installer:
==== codex-shaped command, inherited env ====
Get-FileHash : The term 'Get-FileHash' is not recognized as the name of a cmdlet, function, script file, or operable program.
==== codex-shaped command, PSModulePath removed ====
SHA256 9785001B0DCF755EDDB8AF294A373C0B87B2498660F724E76C4D53F9C217C7A3
Why this does not look like an irm | iex encoding problem
I also checked the real installer script without executing it. The direct Invoke-RestMethod result and the piped text had the same length, same SHA-256 over the text, same Get-FileHash index, and both parsed with zero PowerShell parser errors:
directLength=29883
pipeLength=29883
directSha256=ab7d7864801e62f92aa3abf878e3b0aaa8b9f7de03120bc81b029f67281c1842
pipeSha256=ab7d7864801e62f92aa3abf878e3b0aaa8b9f7de03120bc81b029f67281c1842
directParserErrors=0
pipeParserErrors=0
directGetFileHashIndex=2768
pipeGetFileHashIndex=2768
Expected behavior
The standalone Windows update should run reliably when Codex is started from pwsh, without making Windows PowerShell 5.1 inherit a PowerShell 7 module path that breaks module discovery.
Possible fixes
A few possible directions:
- Add
-NoProfile to the Windows standalone update invocation.
- Remove or normalize
PSModulePath before spawning powershell.exe for StandaloneWindows, so Windows PowerShell can rebuild its default WindowsPowerShell module path.
- Prefer
pwsh when Codex itself is running from pwsh and pwsh is available.
- Add a fallback in
install.ps1 that computes SHA-256 using .NET APIs if Get-FileHash is unavailable.
The most targeted fix seems to be normalizing/removing PSModulePath for the powershell.exe child process used by the Windows standalone update action.
I am happy to help with a PR if maintainers agree with the preferred fix direction.
What happened?
While investigating a Windows standalone update failure, I found that the update action launches Windows PowerShell (
powershell.exe) even when Codex itself is started from PowerShell 7 (pwsh). The childpowershell.exeprocess can inherit PowerShell 7 entries inPSModulePath, which breaks module discovery in Windows PowerShell 5.1 and makesGet-FileHashunavailable.The resulting installer failure looks like this:
This does not appear to be caused by
irm ... | iexcorrupting the installer script text. I verified that the downloaded script text and piped script text are identical and parse successfully.Environment
Current
mainstill has the Windows standalone update action hard-coded as:Observed on
maincommit:Minimal reproduction
This does not execute the Codex installer. It only simulates the relevant process-launch shape and checks whether Windows PowerShell can resolve and run
Get-FileHash.Run this from
pwsh:Actual output
With the inherited environment, Windows PowerShell 5.1 sees PowerShell 7 module paths first and cannot resolve
Get-FileHash:If
PSModulePathis removed before spawningpowershell.exe, Windows PowerShell rebuilds a WindowsPowerShell-only module path andGet-FileHashworks:Codex-shaped safe reproduction
This mirrors the update action shape (
powershell -ExecutionPolicy Bypass -c "... | iex") without running the installer:Why this does not look like an
irm | iexencoding problemI also checked the real installer script without executing it. The direct
Invoke-RestMethodresult and the piped text had the same length, same SHA-256 over the text, sameGet-FileHashindex, and both parsed with zero PowerShell parser errors:Expected behavior
The standalone Windows update should run reliably when Codex is started from
pwsh, without making Windows PowerShell 5.1 inherit a PowerShell 7 module path that breaks module discovery.Possible fixes
A few possible directions:
-NoProfileto the Windows standalone update invocation.PSModulePathbefore spawningpowershell.exeforStandaloneWindows, so Windows PowerShell can rebuild its default WindowsPowerShell module path.pwshwhen Codex itself is running frompwshandpwshis available.install.ps1that computes SHA-256 using .NET APIs ifGet-FileHashis unavailable.The most targeted fix seems to be normalizing/removing
PSModulePathfor thepowershell.exechild process used by the Windows standalone update action.I am happy to help with a PR if maintainers agree with the preferred fix direction.