Description
grep fails on a large share of Windows users. Azure telemetry for 2026-07-22 → 2026-08-05 shows 328 core_failure events across 99 distinct Windows machines (of 617 Windows machines active in the window), all with:
? is not recognized as an internal or external command, operable program or batch file.
plus the German, French, Spanish and Portuguese translations of the same cmd.exe message. Present on released 0.9.2, 0.9.3 and 0.9.4. The ? is our own PII masker — Telemetry.maskString replaces single-quoted text with ?, and cmd.exe quotes the command name, so the identity of the failing binary was destroyed before it reached telemetry.
Root cause
ripgrep's Windows release is a zip, and packages/core/src/ripgrep/binary.ts extracts it by shelling out:
const shell = which("powershell.exe") ?? which("pwsh.exe") ?? "powershell.exe"
When neither resolves, that literal string reaches cross-spawn. parseNonShell() sets needsShell = true whenever resolveCommand() returns undefined and re-spawns through cmd.exe /d /s /c, so cmd.exe answers with the "not recognized" message. throw new Error(result.stderr.trim()) then makes that string the error verbatim.
Because RipgrepBinary.filepath is wrapped in Effect.cached, a single failed extraction breaks grep for the rest of the session.
Note cross-spawn resolves against process.env.PATH, not our augmented PATH — so the affected machines are ones where PowerShell is missing from PATH, not necessarily absent from the machine.
Blast radius
Wider than the grep tool: @opencode-ai/core/ripgrep also backs the HTTP-API file handlers (server/routes/instance/httpapi/handlers/file.ts) and cli/cmd/debug/ripgrep.ts.
glob, ls and skill are unaffected — they use the packages/opencode/src/file/ripgrep.ts shim, which already unzips in-process.
Upstream
Upstream carries the same fragility. anomalyco/opencode#24291 is open, reporting Expand-Archive unusable when spawned from the Bun-compiled binary and affecting grep/glob/skill. Their #23457 fix only corrected how paths were passed to PowerShell (the $args → inlined-and-escaped form we already carry); it did not remove the dependency on PowerShell resolving. upstream/dev still has the same code.
Expected
Extracting ripgrep should not depend on an external shell being resolvable.
Description
grepfails on a large share of Windows users. Azure telemetry for 2026-07-22 → 2026-08-05 shows 328core_failureevents across 99 distinct Windows machines (of 617 Windows machines active in the window), all with:plus the German, French, Spanish and Portuguese translations of the same cmd.exe message. Present on released 0.9.2, 0.9.3 and 0.9.4. The
?is our own PII masker —Telemetry.maskStringreplaces single-quoted text with?, and cmd.exe quotes the command name, so the identity of the failing binary was destroyed before it reached telemetry.Root cause
ripgrep's Windows release is a zip, and
packages/core/src/ripgrep/binary.tsextracts it by shelling out:When neither resolves, that literal string reaches
cross-spawn.parseNonShell()setsneedsShell = truewheneverresolveCommand()returns undefined and re-spawns throughcmd.exe /d /s /c, so cmd.exe answers with the "not recognized" message.throw new Error(result.stderr.trim())then makes that string the error verbatim.Because
RipgrepBinary.filepathis wrapped inEffect.cached, a single failed extraction breaksgrepfor the rest of the session.Note cross-spawn resolves against
process.env.PATH, not our augmented PATH — so the affected machines are ones where PowerShell is missing from PATH, not necessarily absent from the machine.Blast radius
Wider than the
greptool:@opencode-ai/core/ripgrepalso backs the HTTP-API file handlers (server/routes/instance/httpapi/handlers/file.ts) andcli/cmd/debug/ripgrep.ts.glob,lsandskillare unaffected — they use thepackages/opencode/src/file/ripgrep.tsshim, which already unzips in-process.Upstream
Upstream carries the same fragility. anomalyco/opencode#24291 is open, reporting
Expand-Archiveunusable when spawned from the Bun-compiled binary and affectinggrep/glob/skill. Their #23457 fix only corrected how paths were passed to PowerShell (the$args→ inlined-and-escaped form we already carry); it did not remove the dependency on PowerShell resolving.upstream/devstill has the same code.Expected
Extracting ripgrep should not depend on an external shell being resolvable.