Summary
The read tool hangs indefinitely with no error, no timeout, no retry when the file path is on a Windows mount (/mnt/c/..., /mnt/d/..., etc.) under WSL. Same path works flawlessly via the bash tool (cat, head). The session stays in busy status forever until manually aborted.
OS-level access to the same files is instant (time cat /mnt/c/.../file.md → ~16 ms). So the I/O itself is not the cause — the issue is inside the read tool implementation.
Environment
- OS: Windows 11 + WSL2 Ubuntu
- OpenCode: 1.14.50 (Linux x86-64 binary at
~/.opencode/bin/opencode)
- Provider/Model: tested with OpenRouter
deepseek/deepseek-v4-pro AND deepseek/deepseek-v4-flash — same hang for both
- Agent:
build
- Project directory:
/home/mekaret/relkhon-vault (WSL native filesystem)
Reproduction matrix
| Model |
File path target |
Tool requested |
Result |
| deepseek-v4-flash |
/home/mekaret/.../file.md (WSL native) |
read |
✅ completes in ~3 s |
| deepseek-v4-pro |
/home/mekaret/.../file.md (WSL native) |
read |
✅ completes in ~3 s |
| deepseek-v4-flash |
/mnt/c/Users/.../file.md (Windows mount) |
read |
❌ hangs indefinitely (180s+, never returns) |
| deepseek-v4-pro |
/mnt/c/Users/.../file.md (Windows mount) |
read |
❌ hangs indefinitely (180s+, never returns) |
| deepseek-v4-flash |
/mnt/c/Users/.../file.md |
bash (head -1) |
✅ completes in ~1 s |
So the model and the tool-call streaming format are not the issue (other tool calls work in the same session shape). The read tool itself, on /mnt/... paths, never completes.
Log signature
On a working session (/home/ path, read tool):
INFO service=session id=<sid> directory=/home/mekaret/relkhon-vault created
INFO service=session.prompt session.id=<sid> step=0 loop
INFO service=session.processor session.id=<sid> messageID=<mid> process
INFO service=llm providerID=openrouter modelID=deepseek/deepseek-v4-flash session.id=<sid> agent=build mode=primary stream
INFO service=session.prompt session.id=<sid> step=1 loop ← tool result came back
INFO service=session.processor session.id=<sid> messageID=<mid2> process
INFO service=llm ... stream
INFO service=session.prompt session.id=<sid> step=2 loop
INFO service=session.prompt session.id=<sid> exiting loop ← clean completion
On a stuck session (/mnt/c/ path, read tool):
INFO service=session id=<sid> directory=/home/mekaret/relkhon-vault created
INFO service=session.prompt session.id=<sid> step=0 loop
INFO service=session.processor session.id=<sid> messageID=<mid> process
INFO service=llm providerID=openrouter modelID=deepseek/deepseek-v4-pro session.id=<sid> agent=build mode=primary stream
← session.prompt step=1 NEVER FIRES
← no further log entries on this sessionID until manual abort
INFO (~3 min later, manual abort) service=session.prompt session.id=<sid> cancel
ERROR service=session.processor session.id=<sid> error=Aborted process
Inspecting the assistant message via the session API shows the model does produce reasoning + a streaming tool call in state.status: "running", but the call never finalizes. No step=1 event is ever emitted by the prompt loop.
What rules this out
- Not an I/O / mount issue: direct
cat /mnt/c/.../file.md from WSL completes in ~16 ms. The file is fully readable from the WSL side.
- Not a model issue: same model on
/home/ works fine; same /mnt/c/ path hangs across multiple models.
- Not a path resolution issue:
opencode_path_get directory=/home/... returns the expected WSL path. The user prompt contains the path as a literal string — no rewriting happens before the model sees it.
- Not a permission issue: file is
rwxrwxrwx for the running user.
- Not a tool-call streaming issue: the
bash tool works on the same /mnt/c/ path within seconds.
Hypothesis
Something in the read tool implementation does extra processing on the path before serving the file. That extra step blocks on /mnt/... paths but not on /home/.... Candidates I can't verify from the outside (binary is compiled):
- A "stay within project directory" guard that does a
realpath walk and gets stuck (e.g. WSL's realpath on /mnt/c/... triggers something pathological).
- An
fs.watch / fs.stat cache that races on cross-filesystem paths.
- A symlink-resolution loop that hangs on the DrvFs layer.
- An asynchronous file-read using a streaming primitive that is silently dropped on
/mnt/... paths.
The fact that the hang is silent (no error, no log line, no timeout) is the most actionable signal: there's no error handler being hit, which suggests an await that never resolves rather than a thrown exception that's swallowed.
Proposed acceptance criteria
Workaround
Instruct the agent to use the bash tool (cat, head) instead of read for any file outside the project directory. This works but is clunky to enforce in agent prompts — the model defaults to read for file inspection.
Related but distinct
- #16991 — visibility / permissions / git worktree on
/mnt/. Different surface; my issue is specifically the read tool hang.
- #6460 — early read/shell failures with wrong path on new WSL session. Different mechanism; my issue is reproducible on long-running, healthy sessions.
- #16596 — symlink-targeted WSL folders hang. Possibly related root cause (symlink resolution) but my project directory is plain
/home/mekaret/relkhon-vault with no symlink involvement.
Happy to provide additional traces, full server log excerpts, or run targeted strace if useful.
Summary
The
readtool hangs indefinitely with no error, no timeout, no retry when the file path is on a Windows mount (/mnt/c/...,/mnt/d/..., etc.) under WSL. Same path works flawlessly via thebashtool (cat,head). The session stays inbusystatus forever until manually aborted.OS-level access to the same files is instant (
time cat /mnt/c/.../file.md→ ~16 ms). So the I/O itself is not the cause — the issue is inside thereadtool implementation.Environment
~/.opencode/bin/opencode)deepseek/deepseek-v4-proANDdeepseek/deepseek-v4-flash— same hang for bothbuild/home/mekaret/relkhon-vault(WSL native filesystem)Reproduction matrix
/home/mekaret/.../file.md(WSL native)read/home/mekaret/.../file.md(WSL native)read/mnt/c/Users/.../file.md(Windows mount)read/mnt/c/Users/.../file.md(Windows mount)read/mnt/c/Users/.../file.mdbash(head -1)So the model and the tool-call streaming format are not the issue (other tool calls work in the same session shape). The
readtool itself, on/mnt/...paths, never completes.Log signature
On a working session (
/home/path,readtool):On a stuck session (
/mnt/c/path,readtool):Inspecting the assistant message via the session API shows the model does produce reasoning + a streaming tool call in
state.status: "running", but the call never finalizes. Nostep=1event is ever emitted by the prompt loop.What rules this out
cat /mnt/c/.../file.mdfrom WSL completes in ~16 ms. The file is fully readable from the WSL side./home/works fine; same/mnt/c/path hangs across multiple models.opencode_path_get directory=/home/...returns the expected WSL path. The user prompt contains the path as a literal string — no rewriting happens before the model sees it.rwxrwxrwxfor the running user.bashtool works on the same/mnt/c/path within seconds.Hypothesis
Something in the
readtool implementation does extra processing on the path before serving the file. That extra step blocks on/mnt/...paths but not on/home/.... Candidates I can't verify from the outside (binary is compiled):realpathwalk and gets stuck (e.g. WSL'srealpathon/mnt/c/...triggers something pathological).fs.watch/fs.statcache that races on cross-filesystem paths./mnt/...paths.The fact that the hang is silent (no error, no log line, no timeout) is the most actionable signal: there's no error handler being hit, which suggests an await that never resolves rather than a thrown exception that's swallowed.
Proposed acceptance criteria
readtool either completes on/mnt/...paths within a reasonable time, or fails with a clear error (e.g. "path outside project directory: refusing for safety, use bash if you need it").readtool emits astep=1event on the session prompt loop within a configurable timeout (default 30 s), so a hang surfaces asTIMEOUTinstead of an indefinitebusy.Workaround
Instruct the agent to use the
bashtool (cat,head) instead ofreadfor any file outside the project directory. This works but is clunky to enforce in agent prompts — the model defaults toreadfor file inspection.Related but distinct
/mnt/. Different surface; my issue is specifically thereadtool hang./home/mekaret/relkhon-vaultwith no symlink involvement.Happy to provide additional traces, full server log excerpts, or run targeted strace if useful.