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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions