openai / openai/codex

TypeScript SDK drops the Windows PATH when the env has multiple PATH casings

Open
#45,476 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug CLI exec windows-os
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

Summary

On Windows, the TypeScript SDK can drop the caller's entire PATH when the supplied environment contains more than one casing of the variable, spawning the Codex CLI with only the bundled vendor directory on PATH.

Offending code

https://github.com/openai/codex/blob/main/sdk/typescript/src/exec.ts#L476-L483

function pathEnvKey(env: Record<string, string>, platform: NodeJS.Platform): string {
  if (platform !== "win32") {
    return "PATH";
  }

  const matchingKeys = Object.keys(env).filter((key) => key.toLowerCase() === "path");
  return matchingKeys.includes("Path") ? "Path" : (matchingKeys.at(-1) ?? "PATH");
}

prependPathDirs then collapses every other casing away:

https://github.com/openai/codex/blob/main/sdk/typescript/src/exec.ts#L457-L474

const pathKey = pathEnvKey(env, platform);
if (platform === "win32") {
  for (const key of Object.keys(env)) {
    if (key.toLowerCase() === "path" && key !== pathKey) {
      delete env[key];
    }
  }
}

const existingEntries = (env[pathKey] ?? "")
  .split(path.delimiter)
  .filter((entry) => entry.length > 0 && !pathDirs.includes(entry));
env[pathKey] = [...pathDirs, ...existingEntries].join(path.delimiter);

Why it is wrong

pathEnvKey chooses the surviving key by spelling, not by which key actually holds a value:

  • It hard-codes a preference for the exact spelling "Path", even when that entry is empty and a different casing holds the real search path.
  • Otherwise it falls back to matchingKeys.at(-1), i.e. whichever casing happens to be last in JS object insertion order — an ordering that carries no meaning on Windows, where the environment block is case-insensitive.

prependPathDirs then deletes every other casing. Because the key that was kept may be the empty one, the populated value is destroyed rather than merged. The two functions disagree: one picks a key by spelling, the other discards the data under all the rest.

Mixed casings arise routinely on Windows — spreading process.env into a literal, merging config objects from several sources, or reading env dictionaries produced by other tooling. Users hit this through the documented env option on Codex/CodexExec.

What the user sees

The Codex CLI is spawned with a PATH containing only the bundled codex-path directory. Everything the CLI shells out to — git, node, bash, system utilities, and any configured MCP server launched by command name — fails to resolve, typically surfacing as ENOENT or "command not found" errors from inside a Codex turn. The failure is silent at the SDK layer and depends on key insertion order, so it reproduces inconsistently across callers.

Steps to reproduce

import { prependPathDirs } from "@openai/codex-sdk/dist/exec.js";
import path from "node:path";

const env = { PATH: `C:\\Windows${path.delimiter}C:\\Tools`, Path: "" };
prependPathDirs(env, ["C:\\vendor"], "win32");

console.log(env);

Observed:

{ Path: 'C:\\vendor' }

C:\Windows;C:\Tools is gone.

Expected:

{ Path: 'C:\\vendor;C:\\Windows;C:\\Tools' }

The same loss occurs for { PATH: <real value>, path: "" }, where at(-1) selects the empty path. Reversing the insertion order of the two keys makes the value survive, which confirms the outcome is decided by object key order rather than by the environment's meaning.

Expected behaviour

  • Prefer a PATH spelling that actually carries a value over one that is empty.
  • Never discard a populated search path while collapsing duplicate casings.
  • Keep the existing behaviour when several casings are populated (the "Path" spelling continues to win), so no currently passing case changes.

Related

sdk/python/src/openai_codex/client.py (_path_env_key) is a direct port of the same logic and has the same defect. I have kept this report and the accompanying fix scoped to the TypeScript SDK; the Python variant is worth a separate issue.

Introduced in #23786.

I have a branch with a regression test and a fix, and will open a PR referencing this issue.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in sdk/typescript/src/exec.ts, reading pathEnvKey and prependPathDirs together, then run the Windows reproduction from the issue with differently ordered PATH casings. Done means preserving populated PATH entries while prepending the vendor directory and adding a regression test; the report says a branch with this fix already exists.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript
Domain
cli, tooling
Issue type
Bug
Difficulty
2/5
Estimated time
Half a day
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.