Repository navigation
Description
Summary
On Windows, a location whose directory casing differs from its resolved project.directory (same project id, case-insensitive filesystem) makes instruction-source activation recurse infinitely: ancestorDirectories() never matches its stop argument (case-sensitive ===) and dirname("C:\") is a fixed point, so the walk overflows the stack (RangeError: Maximum call stack size exceeded) on every activation/reload. The failure handler then marks instructions unavailable, so that location silently receives no AGENTS.md instructions at all.
Environment
- opencode version: v2.0.14
- OS: Windows 10.0.22631.6199 (win32 x64)
- Terminal:
TERM=xterm-256color(TERM_PROGRAM,COLORTERMunset) - Shell:
C:\WINDOWS\system32\cmd.exe(%ComSpec%); repro commands run in PowerShell 5.1 - Install/channel: global npm install of
@opencode/cli, update channellatest - Active plugins:
ensure-claude-mem,gemini-env(global),claude-mem-opencode,graphify(project) — allactive, all auto-discovered (nopluginskey in any config). The crash happens in the built-inopencode.config.instructionsource, unrelated to plugins.
Reproduction
- On Windows (case-insensitive FS), have a project whose on-disk directory is
C:\dev\krankly, and register a location that invoked the same directory with different casing (e.g., a client started from a historical cwdC:\dev\Krankly). Both locations share the same project id. - Confirm the mismatch:
GET /api/debug/location→[{"directory":"C:\\dev\\krankly"},{"directory":"C:\\dev\\Krankly"},{"directory":"C:\\Users\\<user>"}]GET /api/location?location[directory]=C:\dev\Krankly→project.directory = "C:\\dev\\krankly"(case differs fromdirectory)
- Trigger instruction activation for the mismatched location:
opencode api post /api/location/reload --param "location[directory]=C:\dev\Krankly"(it also fires on plugin/config reloads and watcher-driven rescans). - Read the server log.
Expected Behavior
The ancestor walk should treat start === stop the same way the branch gate does (case-insensitively on Windows, consistent with FSUtil.contains/path.relative), stop at the project root (or at the filesystem root as a safety net), and load that location's AGENTS.md instructions.
Actual Behavior
Every activation logs (4 occurrences in 3 minutes, one per reload cycle):
timestamp=2026-09-22T21:20:09.132Z level=WARN message="failed to activate instruction source" cause="Cause([Die(RangeError: Maximum call stack size exceeded.)])"
timestamp=2026-09-22T21:22:01.753Z level=WARN message="failed to activate instruction source" cause="Cause([Die(RangeError: Maximum call stack size exceeded.)])"
timestamp=2026-09-22T21:22:08.009Z level=WARN message="failed to activate instruction source" cause="Cause([Die(RangeError: Maximum call stack size exceeded.)])"
timestamp=2026-09-22T21:23:32.407Z level=WARN message="failed to activate instruction source" cause="Cause([Die(RangeError: Maximum call stack size exceeded.)])"
The catch then runs discovery.transform((editor) => editor.unavailable()): sessions under the C:\dev\Krankly location get no project AGENTS.md (verified — project rules were absent from that location's context while the identically-cased C:\dev\krankly location loaded them fine). The sibling warning failed to load instruction source never appears, confirming the crash happens before any source read (in the candidate-subscription walk, not in fs.up).
Additional Context
Root cause in the shipped file packages/core/src/config/plugin/instruction.ts (present at tag v2.0.14):
const project = discovery.project && FSUtil.contains(root, start) // case-insensitive gate → true
const stop = FSUtil.contains(home, start) ? home : root // root = "C:\dev\krankly"
// start = "C:\dev\Krankly"
const candidates = [...(project ? ancestorDirectories(start, stop) ... : [])]
function ancestorDirectories(start: string, stop: string): string[] {
if (start === stop) return [start] // case-sensitive → never true
return [start, ...ancestorDirectories(dirname(start), stop)] // dirname("C:\") === "C:\" → infinite recursion
}FSUtil.containsusesrelative()(case-insensitive on win32), so the walk IS entered;start === stopcompares raw strings and never matchesC:\dev\KranklyvsC:\dev\krankly; walking up reachesC:\, wheredirname("C:\") === "C:\"is a fixed point with no base case → stack overflow.- Suggested fix: compare
start/stopwith the same case-folding used bycontains(win32 case-insensitive), and/or add theparent === currentbase case thatFSUtil.up/findUp/globUpalready have:
function ancestorDirectories(start: string, stop: string): string[] {
const eq = (a: string, b: string) => (process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b)
const out: string[] = []
for (;;) {
out.push(start)
if (eq(start, stop)) return out
const parent = dirname(start)
if (parent === start) return out // filesystem root reached — stop instead of recursing
start = parent
}
}- Note on current
dev:packages/core/src/config/plugin/instruction.tsno longer exists (logic moved topackages/core/src/instruction-context.ts), which uses the iterative, root-guardedfs.up(). Two follow-ups: (1) confirm which releases up to v2.0.14 are affected and ship a fix/patch; (2)fs.up'soptions.stop === currentcomparison is still case-sensitive, so a case-differingstart/stopwalks all the way to the filesystem root instead of stopping at the project root (no crash, but it can pick upAGENTS.mdfiles above the project). - Frequency: consistent — fires on every activation/reload for the affected location.
- Workarounds: keep every client's cwd casing identical to the on-disk casing (prevents the mismatched location from being re-registered) and restart the service to drop in-memory locations.
OPENCODE_DISABLE_PROJECT_CONFIG=1skips the walk but disables project instructions everywhere. - Recent changes: observed immediately after upgrading from v1.8.x to v2.0.14 (forced upgrade).
Activity
github-actions commentedon Sep 22, 2026
This issue might be a duplicate of existing issues. Please check:
core/instructionsactivation to fail withInstructions.InitializationBlockedRangeError: Maximum call stack size exceededin instruction source activation on Windows, also resulting in sessions unable to start