Title
Korn Shell Error popup on Windows when opening Stats — macOS security command dispatched via ksh from MKS Toolkit
Summary
On Windows, opening the Stats view fires a modal error dialog titled "Korn Shell Error" with the message Command /C and Shell Error: /C: not found. Clicking OK dismisses it and Stats continues to load (the fallback to ~/.claude/.credentials.json works). But the popup fires every time the Stats view is opened.
The root cause is in claude-auth.js — readFromKeychain() runs security find-generic-password ... via execSync. There is a if (process.platform !== 'darwin') return null; guard at line 25, so the intent is clearly for the Keychain read to be macOS-only. But on this Windows machine the guard doesn't prevent the popup — I suspect Node's default shell resolution for execSync is picking up MKS Toolkit's ksh.exe (installed at C:\Program Files (x86)\MKS Toolkit\) somewhere in the call chain, and ksh is what's actually surfacing the "not found" popup. security isn't a Windows binary, so something is trying to run it on Windows despite the platform guard.
I haven't been able to reproduce it without MKS on PATH, so it likely only affects the (small) group of Windows users who have legacy Unix-on-Windows toolkits installed. Common in enterprises with mainframe / DataStage / older ETL stacks.
Reproduction
- Windows 11 machine that has MKS Toolkit installed with
C:\Program Files (x86)\MKS Toolkit\mksnt on PATH.
- Install Switchboard
v0.0.30.
- Launch the app.
- Click the Stats icon in the sidebar.
Expected: Stats view opens silently, populated from either Keychain (macOS) or .credentials.json (Windows/Linux).
Actual: A modal dialog titled "Korn Shell Error" pops up:
Command /C
Shell Error: /C: not found
After clicking OK the Stats view loads normally (fallback works).
Environment
- OS: Windows 11 Enterprise (Build 26100)
- Switchboard version: 0.0.30 (Windows NSIS installer)
- MKS Toolkit: 9.x at
C:\Program Files (x86)\MKS Toolkit\mksnt\ksh.exe (Mar 2011 build, on user PATH)
- Claude Code CLI: installed,
~/.claude/.credentials.json exists
Hypothesis / suggested fix
readFromKeychain() already has if (process.platform !== 'darwin') return null; at the top, so on paper the execSync('security ...') call should never execute on Windows. But the popup fires anyway, which suggests either:
- The guard is being bypassed by another code path (some other
execSync on Windows that ends up hitting security or another Mac-only command), or
- There's an earlier
require/init path that touches the Keychain code before the guard runs.
I only did a shallow read of claude-auth.js and main.js, so I couldn't pinpoint the exact call. But since the fallback (readFromFile) already works fine on Windows, the fix is presumably to make sure no security invocation ever runs unless process.platform === 'darwin' — belt-and-braces guard around the execSync itself, not just the enclosing function.
A secondary hardening: when calling execSync on Windows, explicitly pass { shell: process.env.COMSPEC || 'cmd.exe' } so Node doesn't accidentally dispatch to a non-cmd.exe shell that happens to be on PATH. This would defend against MKS/Cygwin/Git-Bash sh intercepting the call.
Impact
Cosmetic-only — Stats still loads via the file-based fallback. But the popup fires on every Stats open, which is disruptive. Very small user population affected (Windows + legacy Unix toolkit).
Title
Korn Shell Errorpopup on Windows when opening Stats — macOSsecuritycommand dispatched via ksh from MKS ToolkitSummary
On Windows, opening the Stats view fires a modal error dialog titled "Korn Shell Error" with the message
Command /CandShell Error: /C: not found. Clicking OK dismisses it and Stats continues to load (the fallback to~/.claude/.credentials.jsonworks). But the popup fires every time the Stats view is opened.The root cause is in
claude-auth.js—readFromKeychain()runssecurity find-generic-password ...viaexecSync. There is aif (process.platform !== 'darwin') return null;guard at line 25, so the intent is clearly for the Keychain read to be macOS-only. But on this Windows machine the guard doesn't prevent the popup — I suspect Node's default shell resolution forexecSyncis picking up MKS Toolkit'sksh.exe(installed atC:\Program Files (x86)\MKS Toolkit\) somewhere in the call chain, and ksh is what's actually surfacing the "not found" popup.securityisn't a Windows binary, so something is trying to run it on Windows despite the platform guard.I haven't been able to reproduce it without MKS on PATH, so it likely only affects the (small) group of Windows users who have legacy Unix-on-Windows toolkits installed. Common in enterprises with mainframe / DataStage / older ETL stacks.
Reproduction
C:\Program Files (x86)\MKS Toolkit\mksnton PATH.v0.0.30.Expected: Stats view opens silently, populated from either Keychain (macOS) or
.credentials.json(Windows/Linux).Actual: A modal dialog titled "Korn Shell Error" pops up:
After clicking OK the Stats view loads normally (fallback works).
Environment
C:\Program Files (x86)\MKS Toolkit\mksnt\ksh.exe(Mar 2011 build, on user PATH)~/.claude/.credentials.jsonexistsHypothesis / suggested fix
readFromKeychain()already hasif (process.platform !== 'darwin') return null;at the top, so on paper theexecSync('security ...')call should never execute on Windows. But the popup fires anyway, which suggests either:execSyncon Windows that ends up hittingsecurityor another Mac-only command), orrequire/init path that touches the Keychain code before the guard runs.I only did a shallow read of
claude-auth.jsandmain.js, so I couldn't pinpoint the exact call. But since the fallback (readFromFile) already works fine on Windows, the fix is presumably to make sure nosecurityinvocation ever runs unlessprocess.platform === 'darwin'— belt-and-braces guard around theexecSyncitself, not just the enclosing function.A secondary hardening: when calling
execSyncon Windows, explicitly pass{ shell: process.env.COMSPEC || 'cmd.exe' }so Node doesn't accidentally dispatch to a non-cmd.exeshell that happens to be on PATH. This would defend against MKS/Cygwin/Git-Bashshintercepting the call.Impact
Cosmetic-only — Stats still loads via the file-based fallback. But the popup fires on every Stats open, which is disruptive. Very small user population affected (Windows + legacy Unix toolkit).