Skip to content

Commit 0d05a0d

Browse files
fix(diagnostics): classify zsh 'command not found: docker' as CLI-not-found (Codex P2 r3)
zsh's login-shell wording is reversed — 'zsh:1: command not found: docker' (name AFTER the colon), not bash/sh's 'docker: command not found'. The CLI-not- found case only matched the bash shape, so the common macOS #696 scenario fell through to the generic 'command not found' branch and recorded MCPX_DOCKER_EXEC_NOT_FOUND, conflating host-CLI-missing (#696) with in-container exec ENOENT. Match 'command not found: docker' in both the isolation-spawn helper and the unhinted classifyDocker fallback. Added a classifier table case for the zsh wording (verified red→green: routed to EXEC_NOT_FOUND without the fix, CLI_NOT_FOUND with it). Related #696 Co-Authored-By: Paperclip <noreply@paperclip.ing>
1 parent 804e09f commit 0d05a0d

2 files changed

Lines changed: 15 additions & 1 deletion

File tree

internal/diagnostics/classifier.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func classifyDocker(err error, _ ClassifierHints) Code {
125125
case strings.Contains(msg, "docker not found in path"),
126126
strings.Contains(msg, "docker not found in login shell"),
127127
strings.Contains(msg, "docker: command not found"),
128+
strings.Contains(msg, "command not found: docker"), // zsh: "zsh:1: command not found: docker"
128129
strings.Contains(msg, "docker: not found"),
129130
strings.Contains(msg, `"docker": executable file not found`):
130131
return DockerCLINotFound
@@ -192,9 +193,13 @@ func classifyDockerIsolatedSpawn(err error) Code {
192193
msg := strings.ToLower(err.Error())
193194
switch {
194195
// (1) docker binary unresolved: shellwrap resolution failure, or the shell
195-
// / Go exec layer reporting `docker` itself missing.
196+
// / Go exec layer reporting `docker` itself missing. Cover both shell
197+
// wordings: bash/sh `docker: command not found` AND zsh's reversed
198+
// `zsh:1: command not found: docker` (the common macOS login-shell shape) —
199+
// the latter must beat the generic "command not found" → EXEC case below.
196200
case strings.Contains(msg, `"docker": executable file not found`),
197201
strings.Contains(msg, "docker: command not found"),
202+
strings.Contains(msg, "command not found: docker"),
198203
strings.Contains(msg, "docker: not found"),
199204
strings.Contains(msg, "docker not found in path"),
200205
strings.Contains(msg, "docker not found in login shell"):

internal/diagnostics/classifier_domains_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ func TestClassify_Docker_IsolationSpawn(t *testing.T) {
9797
hint: ClassifierHints{Transport: "stdio", DockerIsolated: true},
9898
want: DockerCLINotFound,
9999
},
100+
{
101+
// #696 via zsh login shell: the common macOS shape is the REVERSED
102+
// wording `zsh:1: command not found: docker` (name after the colon),
103+
// which must still classify as CLI-not-found, not in-container EXEC.
104+
name: "cli_not_found_zsh_reversed",
105+
err: errors.New("stdio transport (docker_isolation=true): recent stderr: zsh:1: command not found: docker"),
106+
hint: ClassifierHints{Transport: "stdio", DockerIsolated: true},
107+
want: DockerCLINotFound,
108+
},
100109
{
101110
// shellwrap resolution failure surfaces this even without the hint.
102111
name: "cli_not_found_resolve",

0 commit comments

Comments
 (0)