Skip to content

[Fix Prompted Included] Codex path resolution broken with fish shell #218

@DarthNerdus

Description

@DarthNerdus

Issue

Attempts to start any chat fail with the error:

The value of "err" is out of range. It must be a negative integer. Received 1

This reproduces in:

  • the released desktop app
  • npx t3@alpha
  • local development via bun run start

Server logs show a related provider startup failure:

[19:06:59.073] WARN (#80): provider command reactor failed to process event {
  eventType: 'thread.turn-start-requested',
  cause: 'Error: Provider adapter process error (codex) for thread eaa830eb-d987-4317-9fbc-12d5c9af8f0f: The value of "err" is out of range. It must be a negative integer. Received 1\n' +
  [...]

Once a thread hits this failure, it appears stuck in an errored state and does not recover cleanly.

Root cause

This is a macOS PATH normalization bug that is especially visible for users whose default shell is fish.

T3 refreshes PATH by asking the user shell to print it. The current implementation uses:

echo -n $PATH

That works in bash/zsh, but in fish, $PATH is a list. Unquoted expansion becomes space-delimited instead of :-delimited. That corrupts process.env.PATH, and later spawn("codex") / spawn("git") can fail with the misleading Node error:

The value of "err" is out of range. It must be a negative integer. Received 1

There are two fixes needed:

  1. Quote $PATH when reading it from the shell

    • use printf %s "$PATH" instead of echo -n $PATH
  2. Fix both macOS PATH bootstrap codepaths and improve shell fallback

    • apps/server/src/os-jank.ts
    • apps/desktop/src/fixPath.ts

    The desktop app needs an additional fallback because packaged GUI apps may not reliably inherit SHELL. It should resolve the shell using:

    • process.env.SHELL?.trim()
    • else os.userInfo().shell?.trim()
    • else "/bin/zsh"

Without fixing both places, the bug may be resolved for CLI/dev usage but still reproduce in the packaged macOS app.

Workarounds

For CLI/dev usage, launching with a non-fish SHELL avoids the issue:

SHELL=/bin/zsh bun run start
SHELL=/bin/zsh npx t3@alpha

For the packaged app, launching the binary from Terminal also works around it:

SHELL=/bin/zsh '/Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)'

Note: double-click launching from Finder may still fail until the desktop-side fix lands, because Finder-launched apps do not reliably inherit the same shell environment.

Resolution

Update both macOS PATH refresh implementations so they:

  • resolve the login shell via:
    • process.env.SHELL?.trim()
    • fallback to os.userInfo().shell?.trim()
    • fallback to "/bin/zsh"
  • read PATH with:
    • printf %s "$PATH"

Codex fix prompt

This prompt was confirmed to one-shot a fix, but it applies the fix (and function for path resolution) to both server and desktop, instead of extracting to a shared helped. I defer to the project team to apply an additional turn for the appropriate consolidation if desired.

Codex prompt

Update the macOS PATH refresh logic in **both** of these files:

- `apps/server/src/os-jank.ts`
- `apps/desktop/src/fixPath.ts`

In both places:

1. Stop using:

execFileSync(shell, ["-ilc", "echo -n $PATH"], {
  encoding: "utf8",
  timeout: 5000,
});

2. Resolve the shell more robustly:

const envShell = process.env.SHELL?.trim();
if (envShell) return envShell;

try {
  const userShell = os.userInfo().shell?.trim();
  if (userShell) return userShell;
} catch {
  // ignore
}

return "/bin/zsh";

3. Read PATH using:

execFileSync(shell, ["-ilc", 'printf %s "$PATH"'], {
  encoding: "utf8",
  timeout: 5000,
});

Why:

- `"$PATH"` preserves the shell’s delimiter formatting
- in `fish`, that preserves the correct exported colon-delimited PATH
- `printf` is safer and more predictable than `echo`
- packaged macOS GUI apps may not have a usable `process.env.SHELL`, so `os.userInfo().shell` is needed as a fallback

Also add regression tests for both codepaths:

- `apps/server/src/os-jank.test.ts`
- `apps/desktop/src/fixPath.test.ts`

Test cases to cover:

1. On macOS, PATH is read using:

["-ilc", 'printf %s "$PATH"']

2. If `process.env.SHELL` is present, that shell is used.
3. If `process.env.SHELL` is missing, fall back to `os.userInfo().shell`.
4. On non-macOS platforms, the function is a no-op.

Please run:

cd apps/server && bun run test src/os-jank.test.ts
cd apps/desktop && bun run test src/fixPath.test.ts
bun lint
bun typecheck

Validation

  • Running T3 with an explicit shell override (for example SHELL=/bin/zsh npx t3@alpha) avoids the failure.
  • Running local development after applying the fixes restores chat startup.
  • Building the macOS distribution artifact and running the packaged app works after applying the fixes in both places.
  • Using the supplied Codex prompt one-shot the fix for both web and desktop.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions