Skip to content

[Bug]: A missing custom Binary path is reported as the default CLI not found on PATH (Claude, Grok, Kiro, OpenCode, Pi, fx, Muse Code) #35373

Closed as not planned

Description

@coygeek

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

Use a dependency-installed source checkout at the commit under Version or commit. Save the script below outside the checkout as repro.sh and run it from the checkout root with sh /path/to/repro.sh. It creates and removes a private fixture directory beside the checkout (SCRATCH selects another parent), sets private HOME, TMPDIR and T3CODE_HOME, and does not start a server or contact a real provider.

The script decodes enabled settings for each provider with the same nonexistent absolute binaryPath, calls the exported production status functions with real subprocess services, runs the Settings summary function (getProviderSummary) on each result, and runs the real Codex status probe with the same path as a control. Exit 0 means the seven default-name messages are present and the Codex control names the configured path. OpenCode's model-loading continuation is unused because the real version spawn fails first.

Reproduction script: repro.sh
#!/bin/sh
set -eu
CHECKOUT=${CHECKOUT:-$(pwd)}
CHECKOUT=$(cd "$CHECKOUT" && pwd)
SCRATCH=${SCRATCH:-$(dirname "$CHECKOUT")}
fixture=$(mktemp -d "$SCRATCH/provider-message.XXXXXX")
trap 'rm -rf "$fixture"' EXIT HUP INT TERM
mkdir -p "$fixture/profile" "$fixture/tmp" "$fixture/t3"
export HOME="$fixture/profile" TMPDIR="$fixture/tmp" T3CODE_HOME="$fixture/t3" CHECKOUT
export REPRO_FIXTURE="$fixture"
cd "$CHECKOUT/apps/server"
env -u NODE_ENV node --input-type=module <<'JS'
import assert from 'node:assert/strict';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
import * as Effect from 'effect/Effect';
import * as Schema from 'effect/Schema';
import * as NodeServices from '@effect/platform-node/NodeServices';
import * as NodeCrypto from '@effect/platform-node/NodeCrypto';
import * as NodeHttpClient from '@effect/platform-node/NodeHttpClient';
import { ClaudeSettings, CodexSettings } from '@t3tools/contracts';
const root = process.env.CHECKOUT;
const load = (file) => import(pathToFileURL(path.join(root,file)).href);
const customPath = path.join(process.env.REPRO_FIXTURE,'absent-provider-command');
const environment = {HOME:process.env.HOME,TMPDIR:process.env.TMPDIR,PATH:'/usr/bin:/bin'};
const {checkClaudeProviderStatus} = await load('apps/server/src/provider/ClaudeProvider.ts');
const {checkCodexProviderStatus} = await load('apps/server/src/provider/CodexProvider.ts');
const summary = process.env.SKIP_UI_SUMMARY ? undefined : await load('apps/web/src/components/settings/providerStatus.ts');
function check(label, snapshot, expected) {
 assert.equal(snapshot.installed,false);
 assert.equal(snapshot.status,'error');
 assert.equal(snapshot.message,expected);
 assert.equal(snapshot.message.includes(customPath),false);
 console.log(label+': '+JSON.stringify({installed:snapshot.installed,status:snapshot.status,message:snapshot.message}));
 if(!summary) return;
 const displayed=summary.getProviderSummary(snapshot);
 assert.equal(displayed.headline,'Not found');
 assert.equal(displayed.detail,expected);
 console.log(label+'_SETTINGS: '+displayed.headline+' · '+displayed.detail);
}
console.log('RUNTIME: '+process.version+' '+process.platform+' '+process.arch);
await Effect.runPromise(Effect.gen(function*() {
 const claude = yield* checkClaudeProviderStatus(Schema.decodeUnknownSync(ClaudeSettings)({enabled:true,binaryPath:customPath}),undefined,environment,process.env.REPRO_FIXTURE);
 check('CLAUDE',claude,'Claude Agent CLI (`claude`) was not found on PATH.');
 const cases=[
 ['grok','GrokSettings','checkGrokProviderStatus','Grok CLI (`grok`) is not installed or not on PATH.'],
 ['kiro','KiroSettings','checkKiroProviderStatus','Kiro CLI (`kiro-cli`) is not installed or not on PATH.'],
 ['pi','PiSettings','checkPiProviderStatus','Pi CLI (`pi`) is not installed or not on PATH. Install with `npm install -g --ignore-scripts @earendil-works/pi-coding-agent`.'],
 ['muse','MuseSettings','checkMuseProviderStatus','Muse Code CLI (`muse`) was not found. Install Muse Code and run `muse login` on this T3 server host.'],
 ['fx','FxSettings','checkFxProviderStatus','fx is not installed or not on PATH.']
 ];
 for(const [provider,schemaName,fn,message] of cases){
  const s=yield* Effect.promise(()=>load(`packages/provider-${provider}/src/settings.ts`));
  const status=yield* Effect.promise(()=>load(`packages/provider-${provider}/src/server/status.ts`));
  check(provider.toUpperCase(),yield* status[fn](Schema.decodeUnknownSync(s[schemaName])({enabled:true,binaryPath:customPath}),environment),message);
 }
 const {OpenCodeSettings}=yield* Effect.promise(()=>load('packages/provider-opencode/src/settings.ts'));
 const oc=yield* Effect.promise(()=>load('packages/provider-opencode/src/server/status.ts'));
 const runtime=yield* Effect.promise(()=>load('packages/provider-opencode/src/server/OpenCodeRuntime.ts'));
 const owner=yield* Effect.promise(()=>load('packages/provider-opencode/src/server/OpenCodeServerOwner.ts'));
 const ledger=yield* Effect.promise(()=>load('packages/provider-opencode/src/server/OpenCodeServerLedger.ts'));
 const {probeOpenCodeRuntime}=yield* Effect.promise(()=>load('packages/provider-opencode/src/server/versionProbe.ts'));
 const settings=Schema.decodeUnknownSync(OpenCodeSettings)({enabled:true,binaryPath:customPath});
 const opencode=yield* Effect.gen(function*(){
  const serverOwner=yield* owner.make({binaryPath:customPath,directory:process.env.REPRO_FIXTURE,environment});
  return yield* oc.checkOpenCodeProviderStatus(settings,process.env.REPRO_FIXTURE,probeOpenCodeRuntime(settings,environment),Effect.succeed([])).pipe(Effect.provideService(owner.OpenCodeServerOwner,serverOwner));
 }).pipe(Effect.provide(runtime.layer),Effect.provide(ledger.layer({stateDir:process.env.T3CODE_HOME})));
 check('OPENCODE',opencode,'OpenCode CLI (`opencode`) is not installed or not on PATH.');
 const codex=yield* checkCodexProviderStatus(Schema.decodeUnknownSync(CodexSettings)({enabled:true,binaryPath:customPath}),undefined,environment);
 const expected='Could not start Codex CLI (`'+customPath+'`). Check Settings → Providers → Codex → Binary path on the server. Make sure the configured executable exists and can be run.';
 assert.equal(codex.installed,false);assert.equal(codex.status,'error');assert.equal(codex.message,expected);
 console.log('CODEX_CONTROL: '+JSON.stringify({installed:codex.installed,status:codex.status,message:codex.message.replaceAll(customPath,'$tmp/absent-provider-command')}));
}).pipe(Effect.scoped,Effect.provide(NodeHttpClient.layerUndici),Effect.provide(NodeCrypto.layer),Effect.provide(NodeServices.layer)));
console.log('DEFECT PRESENT: seven actual missing-executable probes name defaults; real Codex control names configured path and setting.');
JS

The corresponding client steps are: enable the provider in Settings → Providers, set its Binary path to a nonexistent absolute executable path, and refresh provider status. For OpenCode, leave Server URL empty so the local CLI is used. These client steps were traced through their production consumers; no browser session was run.

Expected behavior

When the configured executable cannot be started, the diagnostic identifies that executable and directs the user to the instance's Binary path rather than asserting that the default CLI is absent from PATH. Claude's Binary path setting describes the value as the path to the Claude binary used by this instance, and runClaudeCommand passes it to the subprocess resolver; the other affected providers expose the same configurable Binary path in their packages/provider-*/src/settings.ts.

Codex's missing-executable diagnostic already names the attempted path and the Settings → Providers → Codex → Binary path setting. PR #11345 introduced that wording for Codex only, and its discussion does not promise the same change for every provider, so this report is not a regression claim against that PR; it reports the incorrect command identity in the other providers as an independent misleading diagnostic.

Actual behavior

Summary. When a provider instance has a custom absolute Binary path that does not exist, the Claude, Grok, Kiro, OpenCode, Pi, fx and Muse Code status probes attempt that configured executable but report a fixed message naming the default command, for example Claude Agent CLI (`claude`) was not found on PATH. The message omits the executable that was actually attempted and points the user at PATH and the default CLI instead of the instance's Binary path. Codex already reports the configured path and the relevant setting in the same situation. This is a misleading diagnostic only: the providers correctly report installed: false and status: "error" for the absent executable.

All seven providers return installed: false, status: "error", and a fixed default-name message that omits the attempted executable. For Claude the message is exactly Claude Agent CLI (`claude`) was not found on PATH. although the command attempted was the configured $tmp/absent-provider-command. With the same input, Codex returns Could not start Codex CLI (`$tmp/absent-provider-command`). Check Settings → Providers → Codex → Binary path on the server. Make sure the configured executable exists and can be run.

The executed Settings summary function returns headline Not found with the same detail for every affected provider, for example Not found · Claude Agent CLI (`claude`) was not found on PATH., and the provider card displays them. By source tracing, the chat banner's getProviderStatusMessage returns the same status.message under the title Claude provider status. No browser rendering was observed.

The fixed-message branches are in Claude, Grok, Kiro, OpenCode, Pi, fx and Muse Code. They are reached from the production drivers (Claude, Grok, Kiro, OpenCode, Pi, fx, Muse Code); with an enabled instance and a local executable path the failure occurs before authentication or model discovery, and the server snapshot builder passes the probe message to clients unchanged. These locators are breadcrumbs, not a prescribed fix.

Evidence

  • Expected source: the instance Binary path setting in packages/contracts/src/settings.ts lines 771-777 and its use in apps/server/src/provider/ClaudeProvider.ts lines 565-582, with the Codex diagnostic in apps/server/src/provider/CodexProvider.ts lines 870-883 as the working comparison.
  • Failure source: the reproduction output above, produced by the exported production status functions for the seven providers and the Settings summary function.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The script above passed from a clean checkout at the baseline. It also passed on the upstream main revision under Version or commit, where these seven messages are unchanged; changes there since the baseline in these providers concern model discovery, home-directory expansion, sign-in guidance and incomplete Muse installs. That upstream run reused the baseline's installed third-party dependencies and added one missing workspace link for a newly declared dependency in the disposable checkout.

Related

These concern different behavior: #11345 and #10514 concern Codex-only setup guidance; #35150 and #29330 concern ~ expansion in Binary path, which this absolute-path input does not exercise; #29015 was an MSIX private-install environment issue; #22873 and #22763 concern stderr loss after a process starts; #34354, #34514, #28714, #28890 and #28904 concern PATH capture and refresh.

Restoration check

With the script's input, each of the seven providers still returns installed: false and status: "error", but its message identifies the configured executable that could not be started (the attempted absolute path) and points to that instance's Binary path setting rather than asserting the default command is missing from PATH; the Settings summary shows that message. The Codex control continues to name the configured path and setting. The script's current assertions encode the defect and will fail once it is fixed.

Impact

Cosmetic issue

Version or commit

T3 Code 0.1.9 at d9805b2, rerun from a clean checkout; also reproduced on upstream main at 4fd40e1. On current main at 4e48d9e the seven message strings are unchanged in source (not rerun there).

Environment

macOS on arm64, Node v26.12.1, exported production status functions with real subprocess services and an isolated process environment. No provider credentials, authenticated turn, desktop app, mobile client or browser session was used.

Logs or stack traces

The script's asserted output at the baseline, with warning stacks for the expected missing-process errors omitted and $tmp/absent-provider-command replacing the generated fixture path; each provider's _SETTINGS line reads Not found · <same message>:

RUNTIME: v26.12.1 darwin arm64
CLAUDE: {"installed":false,"status":"error","message":"Claude Agent CLI (`claude`) was not found on PATH."}
GROK: {"installed":false,"status":"error","message":"Grok CLI (`grok`) is not installed or not on PATH."}
KIRO: {"installed":false,"status":"error","message":"Kiro CLI (`kiro-cli`) is not installed or not on PATH."}
PI: {"installed":false,"status":"error","message":"Pi CLI (`pi`) is not installed or not on PATH. Install with `npm install -g --ignore-scripts @earendil-works/pi-coding-agent`."}
MUSE: {"installed":false,"status":"error","message":"Muse Code CLI (`muse`) was not found. Install Muse Code and run `muse login` on this T3 server host."}
FX: {"installed":false,"status":"error","message":"fx is not installed or not on PATH."}
OPENCODE: {"installed":false,"status":"error","message":"OpenCode CLI (`opencode`) is not installed or not on PATH."}
CODEX_CONTROL: {"installed":false,"status":"error","message":"Could not start Codex CLI (`$tmp/absent-provider-command`). Check Settings → Providers → Codex → Binary path on the server. Make sure the configured executable exists and can be run."}
DEFECT PRESENT: seven actual missing-executable probes name defaults; real Codex control names configured path and setting.

Screenshots, recordings, or supporting files

The rerunnable script is included under Steps to reproduce. No screenshots or recordings were captured.

Workaround

Inspect the affected instance's Binary path on the server, correct it to a runnable executable and refresh provider status. Installing the default command on PATH alone does not repair a still-configured nonexistent absolute path. Authenticated generation after correcting a real installation was not tested.

Activity

  1. juliusmarminge commented on Nov 12, 2026

    @juliusmarminge
    Member

    I tried to reproduce this on the current build and could not.

    Not run. I stopped before running anything: the worktree checkout of origin/main (4e48d9e) at ~/wt/factory-issue-35373-triage was still populating files, so vp i and the reporter's repro.sh never ran. I read the code from origin/main at 4e48d9e instead. In apps/server/src/provider/ClaudeProvider.ts, the status probe runs claudeSettings.binaryPath (line 572). When that fails with a missing command (isCommandMissingCause, lines 667-672), it returns the fixed message "Claude Agent CLI (claude) was not found on PATH." and never names the configured path. CodexProvider.ts:885 handles the same failure with "Could not start Codex CLI (<binaryPath>). Check Settings → Providers → Codex → Binary path on the server." I did not read the Grok, Kiro, OpenCode, Pi, fx or Muse status.ts branches. No test in the repo asserts any of the seven fixed messages. I wrote no failing test and pushed no branch.

    Closing as not planned. If it still happens for you, reply with the exact steps and the version you are on and it will be looked at again.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions