Shell Integration Warning when using MiMo: Root Cause Analysis and Fix
Environment
- AI Model: MiMo 2.5 Pro (Xiaomi) — long Chain-of-Thought reasoning causes extended wait times between terminal commands, and tendency to maintain long sessions makes these bugs especially prominent
- Zoo Code: v3.65.100202 (
zoocodeorganization.zoo-code)
- OS: Windows 11, PowerShell 5.1
- Target File:
dist/extension.js (15,442,677 bytes)
- Date: 2026-06-28
Summary of Bugs Found
| # |
Bug |
Severity |
User Experience |
Root Cause Function |
| 1 |
Dead terminal reuse infinite loop |
🔴 Critical |
Command execution permanently impossible (10+ consecutive failures) |
getOrCreateTerminal() |
| 2 |
Missing shellPath fallback |
🔴 Critical |
PowerShell cmdlets don't execute on Windows |
Ln constructor |
| 3 |
execa provider cmd.exe fallback |
🔴 Critical |
git commit and other commands with quotes fail |
xit.run() |
| 4 |
Intermittent command failures (3 causes) |
🟠 High |
Same command alternates between success/failure (~35% failure rate) |
getOrCreateTerminal(), catch block, Olr.execute() |
| 5 |
Missing customCwd type validation |
🟠 High |
Terminal state permanently corrupted, all commands fail |
CFi() |
Bug 1: Dead Terminal Reuse Infinite Loop
Symptom
When executing commands via execute_command, if the terminal process is dead (user closed the panel, shell integration initialization failed, etc.), Zoo Code enters an infinite loop reusing the same dead terminal.
Command attempt → Terminal process already dead → ShellIntegrationError
→ Error handler retries with same taskId → getOrCreateTerminal returns same dead terminal
→ ShellIntegrationError again → infinite loop
Actual error message:
Command failed to execute in terminal due to a shell integration error.
VS Code UI warning:
Your command is being executed without VSCode terminal shell integration.
Real reproduction timeline (23 attempts in 16 minutes, 15 failures — 35% success rate):
[05:22:47] python analyze_script.py → ✅ success
[05:23:07] powershell Get-ChildItem → ❌ "shell integration error"
[05:23:25] python analyze_script.py → ❌ "shell integration error"
[05:23:32] python analyze_script.py → ❌ "shell integration error"
... (10 consecutive failures) ...
[05:24:49] python analyze_script.py → ✅ success (new terminal assigned)
[05:25:11] python -c "..." → ❌ "shell integration error"
[05:25:19] python -c "..." → ✅ success
[05:25:26] python -c "..." → ❌ "shell integration error"
Key observation: success/failure occurs regardless of command content. echo "test" succeeds, then python -c "print('test')" fails immediately after.
Root Cause Code (extension.js, getOrCreateTerminal(), offset ~14,921,404)
Call chain:
Olr.execute() [execute_command tool handler]
└─ try { CFi(r, w) } [terminal command execution]
├─ Fh.getOrCreateTerminal(c, t.taskId, N) [find or create terminal]
└─ Y.runCommand(r, Q) [execute command in terminal]
└─ onNoShellIntegration → new Yq() [ShellIntegrationError thrown]
└─ catch(E)
└─ FNl(E) → true → CFi(r, {...w, terminalShellIntegrationDisabled:!0})
⚠️ Same dead terminal returned → infinite loop
getOrCreateTerminal()'s find() searches by taskId + provider + reuseKey + cwd, but dead terminals also satisfy all these conditions. There's no isClosed() check, so dead terminals are returned as-is.
// ❌ Original code — getOrCreateTerminal() (offset ~14,921,404)
static async getOrCreateTerminal(e, r, a="vscode") {
let o = this.getAllTerminals(),
s = a === "vscode" ? Ln.getReuseKey() : a,
n;
return r && (n = o.find(l => {
if (l.busy || l.taskId !== r || l.provider !== a || l.reuseKey !== s) return !1;
let c = l.getCurrentWorkingDirectory();
return c ? jo(Rq.Uri.file(e).fsPath, c) : !1
})),
n || (n = o.find(l => {
if (l.busy || l.provider !== a || l.reuseKey !== s) return !1;
let c = l.getCurrentWorkingDirectory();
return c ? jo(Rq.Uri.file(e).fsPath, c) : !1
})),
n || (n = this.createTerminal(e, a)),
n.taskId = r,
n
}
The error handler also doesn't create a new terminal on retry:
// ❌ Original code — catch block (offset ~15,077,884)
catch(E) {
if (r.supersedePendingAsk(), FNl(E)) {
let m = {executionId: v, status: "fallback"};
x?.postMessageToWebview({type: "commandExecutionStatus", text: JSON.stringify(m)});
let [g, y] = await CFi(r, {...w, terminalShellIntegrationDisabled: !0});
// ⚠️ No forceNewTerminal option → getOrCreateTerminal returns same dead terminal
g && (r.didRejectTool = !0), c(y)
}
}
Fix (before/after)
Fix 1/5 — Add 4th parameter to getOrCreateTerminal():
// ✅ After fix — getOrCreateTerminal()
static async getOrCreateTerminal(e, r, a="vscode", __fn=!1) {
let o = this.getAllTerminals(),
s = a === "vscode" ? Ln.getReuseKey() : a,
n;
return __fn || ( // ← When __fn is true, skip find() calls entirely
r && (n = o.find(l => {
if (l.busy || l.isClosed() || l.taskId !== r || l.provider !== a || l.reuseKey !== s) return !1;
// ^^^^^^^^^^ Exclude dead terminals
let c = l.getCurrentWorkingDirectory();
return c ? jo(Rq.Uri.file(e).fsPath, c) : !1
})),
n || (n = o.find(l => {
if (l.busy || l.isClosed() || l.provider !== a || l.reuseKey !== s) return !1;
// ^^^^^^^^^^ Exclude dead terminals
let c = l.getCurrentWorkingDirectory();
return c ? jo(Rq.Uri.file(e).fsPath, c) : !1
}))
),
n || (n = this.createTerminal(e, a)), // ← __fn=true always reaches here → new terminal
n.taskId = r,
n
}
Fix 2/5 — Add forceNewTerminal option to CFi() function (offset ~15,078,569):
// ❌ Original
async function CFi(t, {executionId:e, command:r, customCwd:a,
terminalShellIntegrationDisabled:o=!0, commandExecutionTimeout:s=0, agentTimeout:n=0}) {
// ✅ After fix
async function CFi(t, {executionId:e, command:r, customCwd:a,
terminalShellIntegrationDisabled:o=!0, commandExecutionTimeout:s=0, agentTimeout:n=0,
forceNewTerminal:__fn=!1}) {
Fix 3/5 — Forward __fn to getOrCreateTerminal call (offset ~15,080,813):
// ❌ Original
let Y = await Fh.getOrCreateTerminal(c, t.taskId, N);
// ✅ After fix
let Y = await Fh.getOrCreateTerminal(c, t.taskId, N, __fn);
Fix 4/5 — Enable forceNewTerminal on error handler retry (offset ~15,078,012):
// ❌ Original
let [g, y] = await CFi(r, {...w, terminalShellIntegrationDisabled: !0});
// ✅ After fix
let [g, y] = await CFi(r, {...w, terminalShellIntegrationDisabled: !0, forceNewTerminal: !0});
Fix 5/5 — Include default value in initial options object (offset ~15,077,775):
// ❌ Original
agentTimeout: I};
// ✅ After fix
agentTimeout: I, forceNewTerminal: !1};
Bug 2: Missing shellPath Fallback
Symptom
On Windows, when the VS Code terminal profile is set to cmd.exe or no profile exists, Zoo Code doesn't set shellPath when creating a terminal, falling back to VS Code's default (cmd.exe). Since cmd.exe doesn't support shell integration, all commands fail.
Actual error messages:
Command: Test-Path "README.md"
Result: 'Test-Path' is not recognized as an internal or external command, operable program or batch file.
Command: if (Test-Path "README.md") { (Get-Item "README.md").Length }
Result: ")" was unexpected at this time.
Root Cause Code (extension.js, Ln constructor, offset ~13,190,853)
// ❌ Original code — Ln class constructor
var Ln = class t extends mD {
terminal; cmdCounter = 0; activeShellExecution;
constructor(e, r, a) {
super("vscode", e, a, t.getReuseKey());
let o = t.getEnv(), s = new Tf.ThemeIcon("rocket");
if (r) this.terminal = r;
else {
let n = {cwd: a, name: "Zoo Code", iconPath: s, env: o},
l = t.getProfileShell();
l?.shellPath && ( // ← When shellPath is missing, this entire block is skipped
n.shellPath = l.shellPath,
l.shellArgs && (n.shellArgs = l.shellArgs),
console.info(`[Terminal] Creating terminal with profile "${t.getTerminalProfile()}" -> ${l.shellPath}`),
l.env && (n.env = {...l.env, ...o})
),
this.terminal = Tf.window.createTerminal(n)
// ⚠️ createTerminal called without n.shellPath set
// → VS Code uses cmd.exe (Windows default)
// → No shell integration support → all commands fail
}
}
Fix (before/after)
// ✅ After fix — Ln class constructor
l?.shellPath // ← Changed to ternary operator
? ( // ← Profile has shellPath (preserve existing behavior)
n.shellPath = l.shellPath,
l.shellArgs && (n.shellArgs = l.shellArgs),
console.info(`[Terminal] Creating terminal with profile "${t.getTerminalProfile()}" -> ${l.shellPath}`),
l.env && (n.env = {...l.env, ...o})
)
: ( // ← No shellPath: explicit fallback
n.shellPath = process.platform === "win32"
? (process.env.SystemRoot || "C:\\Windows") + "\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
: process.env.SHELL || "/bin/bash",
console.info(`[Terminal] No profile shellPath found; using fallback: ${n.shellPath}`)
),
this.terminal = Tf.window.createTerminal(n)
Bug 3: execa Provider cmd.exe Fallback
Symptom
When the terminal provider is "execa" instead of "vscode", commands like git commit trigger the shell integration warning. Zoo Code AI detects: "The terminal appears to be running cmd.exe".
Root Cause Code (extension.js, xit.run(), offset ~14,916,022)
// ❌ Original code — execa shell path setting
shell: mD.getExecaShellPath() || !0
// When getExecaShellPath() is undefined → ||!0 → true
// execa with shell:true → Windows COMSPEC → cmd.exe
Fix (before/after)
// ❌ Original
shell: mD.getExecaShellPath() || !0
// ✅ After fix
shell: mD.getExecaShellPath() || (
process.platform === "win32"
? (process.env.SystemRoot || "C:\\Windows") + "\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
: process.env.SHELL || "/bin/bash"
)
Bug 4: Intermittent Command Failures (3 Causes)
Symptom
After fixing the above 3 bugs, command execution still intermittently fails (~35% failure rate). Same command alternates between success and failure regardless of content.
Cause 1: Missing isClosed() check in getOrCreateTerminal()
// ❌ Original — find() without isClosed() check
n = o.find(l => {
if (l.busy || l.taskId !== r || ...) return !1;
// ⚠️ No l.isClosed() check!
})
// ✅ After fix
n = o.find(l => {
if (l.busy || l.isClosed() || l.taskId !== r || ...) return !1;
// ^^^^^^^^^^ Added
})
Cause 2: Missing retry for non-Yq errors
// ❌ Original — catch block
catch(E) {
if (FNl(E)) {
await CFi(r, {...w, forceNewTerminal:!0});
} else {
// non-Yq error → warning only, no retry!
await r.say("shell_integration_warning");
}
}
// ✅ After fix
catch(E) {
if (FNl(E)) {
await CFi(r, {...w, forceNewTerminal:!0});
} else if(!(E instanceof Yq)) {
// non-Yq error also retries with new terminal
await CFi(r, {...w, terminalShellIntegrationDisabled:!0, forceNewTerminal:!0});
} else {
await r.say("shell_integration_warning");
}
}
Cause 3: terminalShellIntegrationDisabled default value problem
// ❌ Original — default true forces execa provider
{terminalShellIntegrationDisabled:N=!0}=R??{}
// ✅ After fix — default false uses vscode provider
{terminalShellIntegrationDisabled:N=!1}=R??{}
Bug 5: Missing customCwd Type Validation
Symptom
When customCwd is passed as an Object instead of string, TypeError occurs. Terminal state becomes permanently corrupted — all subsequent commands fail. Cannot recover even by reloading VS Code.
Stack Trace
TypeError: The "path" argument must be of type string. Received an instance of Object
at Object.isAbsolute (node:path:482:5)
at CFi (extension.js:5086:5100)
at Olr.execute (extension.js:5086:4351)
Fix (before/after)
// ❌ Original
a ? cV.isAbsolute(a) ? c = a : c = cV.resolve(a) : ...
// ✅ After fix
a && typeof a === "string" ? cV.isAbsolute(a) ? c = a : c = cV.resolve(a) : ...
Impact Analysis: MiMo Model Relevance
These bugs affect all AI models. However, MiMo 2.5 Pro's characteristics make them more frequent and more visible:
- Long Chain-of-Thought: Extended wait times between terminal commands → higher shell integration timeout chance
- Iterative tool calls: Many steps → frequent terminal use → dead terminal accumulation
- Long sessions: 30+ minute sessions → failure rate spikes
GPT-4, Claude, Gemini, and other models experience the same bugs under the same conditions. The root cause is in Zoo Code's code, not the AI model.
Verification Results
Before fix (16 minutes): 23 attempts, 15 failures → 35% success rate
After fix: 10 consecutive commands tested, 9/10 success (90%). The 1 failure is a PowerShell 5.x && syntax limitation, not a Zoo Code bug. Zoo Code related errors: 0.
Official Fix Suggestions (TypeScript)
1. Terminal reuse state check
static async getOrCreateTerminal(cwd: string, taskId: string, provider: string = "vscode", forceNew: boolean = false) {
const terminals = this.getAllTerminals();
let terminal: TerminalWrapper | undefined;
if (!forceNew) {
terminal = terminals.find(t => {
if (t.busy || t.isClosed() || t.taskId !== taskId || t.provider !== provider) return false;
// ... cwd matching logic
});
}
if (!terminal) terminal = this.createTerminal(cwd, provider);
terminal.taskId = taskId;
return terminal;
}
2. shellPath fallback chain
const profile = Terminal.getProfileShell();
if (profile?.shellPath) {
options.shellPath = profile.shellPath;
} else {
options.shellPath = process.platform === "win32"
? `${process.env.SystemRoot || "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`
: process.env.SHELL || "/bin/bash";
}
3. execa provider shell path fallback
shell: ExecaTerminal.getExecaShellPath() || (
process.platform === "win32"
? `${process.env.SystemRoot || "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`
: process.env.SHELL || "/bin/bash"
)
4. Error handler non-Yq error retry
catch (error) {
task.supersedePendingAsk();
if (isShellIntegrationError(error) && !error.commandSubmitted) {
// Retry with new terminal
const [rejected, result] = await executeCommandInTerminal(task, {
...options, terminalShellIntegrationDisabled: true, forceNewTerminal: true,
});
resolve(result);
} else if (!(error instanceof ShellIntegrationError)) {
// Non-Yq errors also retry
const [rejected, result] = await executeCommandInTerminal(task, {
...options, terminalShellIntegrationDisabled: true, forceNewTerminal: true,
});
resolve(result);
} else {
await task.say("shell_integration_warning");
resolve("Command failed...");
}
}
5. customCwd type validation
const cwd = customCwd && typeof customCwd === "string"
? (path.isAbsolute(customCwd) ? customCwd : path.resolve(customCwd))
: defaultCwd;
Shell Integration Warning when using MiMo: Root Cause Analysis and Fix
Environment
zoocodeorganization.zoo-code)dist/extension.js(15,442,677 bytes)Summary of Bugs Found
getOrCreateTerminal()Lnconstructorgit commitand other commands with quotes failxit.run()getOrCreateTerminal(), catch block,Olr.execute()CFi()Bug 1: Dead Terminal Reuse Infinite Loop
Symptom
When executing commands via
execute_command, if the terminal process is dead (user closed the panel, shell integration initialization failed, etc.), Zoo Code enters an infinite loop reusing the same dead terminal.Actual error message:
VS Code UI warning:
Real reproduction timeline (23 attempts in 16 minutes, 15 failures — 35% success rate):
Key observation: success/failure occurs regardless of command content.
echo "test"succeeds, thenpython -c "print('test')"fails immediately after.Root Cause Code (extension.js,
getOrCreateTerminal(), offset ~14,921,404)Call chain:
getOrCreateTerminal()'sfind()searches bytaskId + provider + reuseKey + cwd, but dead terminals also satisfy all these conditions. There's noisClosed()check, so dead terminals are returned as-is.The error handler also doesn't create a new terminal on retry:
Fix (before/after)
Fix 1/5 — Add 4th parameter to
getOrCreateTerminal():Fix 2/5 — Add
forceNewTerminaloption toCFi()function (offset ~15,078,569):Fix 3/5 — Forward
__fntogetOrCreateTerminalcall (offset ~15,080,813):Fix 4/5 — Enable
forceNewTerminalon error handler retry (offset ~15,078,012):Fix 5/5 — Include default value in initial options object (offset ~15,077,775):
Bug 2: Missing shellPath Fallback
Symptom
On Windows, when the VS Code terminal profile is set to
cmd.exeor no profile exists, Zoo Code doesn't setshellPathwhen creating a terminal, falling back to VS Code's default (cmd.exe). Sincecmd.exedoesn't support shell integration, all commands fail.Actual error messages:
Root Cause Code (extension.js,
Lnconstructor, offset ~13,190,853)Fix (before/after)
Bug 3: execa Provider cmd.exe Fallback
Symptom
When the terminal provider is
"execa"instead of"vscode", commands likegit committrigger the shell integration warning. Zoo Code AI detects:"The terminal appears to be running cmd.exe".Root Cause Code (extension.js,
xit.run(), offset ~14,916,022)Fix (before/after)
Bug 4: Intermittent Command Failures (3 Causes)
Symptom
After fixing the above 3 bugs, command execution still intermittently fails (~35% failure rate). Same command alternates between success and failure regardless of content.
Cause 1: Missing
isClosed()check ingetOrCreateTerminal()Cause 2: Missing retry for non-Yq errors
Cause 3:
terminalShellIntegrationDisableddefault value problemBug 5: Missing customCwd Type Validation
Symptom
When
customCwdis passed as anObjectinstead ofstring,TypeErroroccurs. Terminal state becomes permanently corrupted — all subsequent commands fail. Cannot recover even by reloading VS Code.Stack Trace
Fix (before/after)
Impact Analysis: MiMo Model Relevance
These bugs affect all AI models. However, MiMo 2.5 Pro's characteristics make them more frequent and more visible:
GPT-4, Claude, Gemini, and other models experience the same bugs under the same conditions. The root cause is in Zoo Code's code, not the AI model.
Verification Results
Before fix (16 minutes): 23 attempts, 15 failures → 35% success rate
After fix: 10 consecutive commands tested, 9/10 success (90%). The 1 failure is a PowerShell 5.x
&&syntax limitation, not a Zoo Code bug. Zoo Code related errors: 0.Official Fix Suggestions (TypeScript)
1. Terminal reuse state check
2. shellPath fallback chain
3. execa provider shell path fallback
4. Error handler non-Yq error retry
5. customCwd type validation