Skip to content

desktop: a timed-out shell env probe at launch leaves the whole session on the minimal PATH #62732

Open

Description

@carson2222

Description

After a reboot, Desktop launched at login and its shell environment probe timed out. Desktop then ran the whole session with launchd's minimal environment, PATH=/usr/bin:/bin:/usr/sbin:/sbin. Every agent shell command lost Homebrew, nvm and other user paths, so gh, node and bun were "command not found" until I restarted the app. Plugins running in Desktop's main process could not find their CLIs either.

The same zsh -il -c 'env -0' takes about 2 s when the machine is idle. Under login load it took longer than the 5 s limit.

This is the V2 version of #49449, which was closed in the V1 triage. The code on dev still behaves the same way:

  • packages/desktop/src/main/service/shell-env.ts: when the -il probe returns ETIMEDOUT, loadShellEnv returns null right away and never tries the -l fallback.
  • packages/desktop/src/main/lifecycle/environment.ts: preferApplicationEnvironment copies the probed variables into process.env only when a probe succeeds, and nothing probes again later. One slow probe at launch decides the environment for the whole session, including the background service it starts.

Expected: a timeout falls back to -l, and a failed probe is retried later (for example in the background, then applied before the next service start), or at least reported in the UI. As it stands, the first sign is agents failing with "command not found".

Plugins

None needed to reproduce. Agent shell commands fail on their own.

OpenCode version

Desktop 2.2.6 (bundled CLI 2.2.6)

Steps to reproduce

  1. Have a login shell whose interactive startup takes more than 5 s. Launching Desktop at login right after a reboot did this for me. From the source, adding sleep 6 to ~/.zshrc should trigger it on demand; I have not tested that variant.
  2. Launch Desktop from Finder or at login.
  3. main.log shows [server] Interactive shell env probe timed out: /bin/zsh.
  4. Ask an agent to run echo $PATH or gh --version. The PATH is /usr/bin:/bin:/usr/sbin:/sbin and Homebrew binaries are not found.
  5. Restart Desktop with an idle machine. The probe succeeds and the PATH is correct again.

Screenshot and/or share link

N/A

Operating System

macOS 27.0.1

Terminal

OpenCode Desktop

Activity

  1. opencodejr commented on Nov 8, 2026

    @opencodejr
    Contributor

    Thanks for the detailed report. I reproduced this by running Desktop's shell-environment loader directly. I don't have macOS here, but the loader has no macOS-specific code path, and shell-env.ts is identical between v2.2.6 and the latest dev (caf8d6a55).

    Setup: a login profile that adds a Homebrew-like directory to PATH, plus an interactive rc that runs sleep 6, in a launchd-like environment (PATH=/usr/bin:/bin:/usr/sbin:/sbin).

    • bash -l on its own finishes quickly with the extra directory on PATH, so the -l fallback would have worked.
    • loadShellEnv logs [server] Interactive shell env probe timed out: /bin/bash and returns null without trying -l.
    • After the startup merge, process.env.PATH stays /usr/bin:/bin:/usr/sbin:/sbin. preferApplicationEnvironment runs only once at startup, so nothing probes again later in the session.

    The script below reproduces it. Related:

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Reproduces anomalyco/opencode#62732 against the desktop shell-env code.
    # Usage: REPO=/path/to/opencode-checkout bash repro.sh
    # A fake HOME whose login profile adds a "Homebrew" dir to PATH, and whose
    # interactive rc takes 6 s (> the 5 s probe timeout), like a slow ~/.zshrc.
    set -euo pipefail
    REPO=${REPO:-/workspace/repo}
    H=$(mktemp -d)
    mkdir -p "$H/brew/bin"; printf '#!/bin/sh\necho gh fake\n' > "$H/brew/bin/gh"; chmod +x "$H/brew/bin/gh"
    cat > "$H/.bash_profile" <<P
    export PATH="$H/brew/bin:\$PATH"
    case \$- in *i*) . "$H/.bashrc";; esac
    P
    echo 'sleep 6' > "$H/.bashrc"
    cat > "$REPO/packages/desktop/repro-62732.ts" <<'T'
    import { NodePath } from "@effect/platform-node"
    import { Effect } from "effect"
    import { loadShellEnv } from "./src/main/service/shell-env"
    const t = Date.now()
    const env = await Effect.runPromise(
      loadShellEnv("/bin/bash").pipe(Effect.provide(NodePath.layer)) as any,
    )
    console.log(`loadShellEnv took ${Date.now() - t} ms, result:`, env === null ? "null" : `PATH=${(env as any).PATH}`)
    // what preferApplicationEnvironment then does: Object.assign(process.env, { ...shellEnv })
    Object.assign(process.env, { ...(env as any) })
    console.log("process.env.PATH after preferApplicationEnvironment-style merge:", process.env.PATH)
    T
    cd "$REPO/packages/desktop"
    echo "== bash -l alone (what the -l fallback would return):"
    env -i HOME="$H" PATH=/usr/bin:/bin:/usr/sbin:/sbin /bin/bash -l -c 'echo PATH=$PATH; command -v gh'
    echo "== loadShellEnv with a launchd-like minimal env:"
    env -i HOME="$H" SHELL=/bin/bash PATH=/usr/bin:/bin:/usr/sbin:/sbin "$(command -v bun)" repro-62732.ts
    rm -f repro-62732.ts

    Run it: bash repro.sh

  2. bunnysayzz commented on Nov 8, 2026

    @bunnysayzz

    Root cause: in packages/desktop/src/main/service/shell-env.ts, loadShellEnv returns null as soon as the -il probe times out. The -l fallback only runs when -il fails outright, never on a timeout, so a slow login shell at launch leaves the session on launchd's minimal PATH.

    Fix is up in #62736: on timeout, fall through to the -l probe. Added a regression test that fails on the old code and passes with the fix (bun test 4/4 in packages/desktop).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions