Skip to content

fix(bash): Validate workdir exists before spawning shell — defend against LLM path hallucination #189

@randomm

Description

@randomm

Problem

When a Claude-family LLM is used as the model powering agents, it sometimes hallucinates directory paths by substituting "Claude" for the actual project directory name ("opencode"). This causes the bash tool to fail with a misleading error:

Error: ENOENT: no such file or directory, posix_spawn '/bin/zsh'

Root Cause

The bash tool (packages/opencode/src/tool/bash.ts:79) accepts an optional workdir parameter from the LLM:

const cwd = params.workdir || Instance.directory

When the LLM explicitly provides workdir, that value is used verbatim with no validation. If the LLM hallucinates a non-existent directory (e.g., /Users/janni/projects/Claude instead of /Users/janni/projects/opencode), child_process.spawn() fails because it cannot chdir into a non-existent directory.

The error message blames /bin/zsh (the shell binary) when the actual problem is the working directory doesn't exist — extremely misleading.

Why It Happens

  • The LLM's self-identity ("I am Claude") contaminates path generation
  • When reconstructing paths from context rather than copying verbatim, the model substitutes its own name
  • This is intermittent: when the LLM omits workdir, Instance.directory (always correct) is used as fallback and commands succeed

Evidence

  • PM agent (Claude Opus) dispatched a task with prompt containing /Users/janni/projects/Claude (hallucinated)
  • Subagent used that path as workdir → all bash commands failed
  • Same commands run without explicit workdir (using Instance.directory fallback) succeed

Fix Required

1. Validate workdir in bash.ts (PRIMARY)

In packages/opencode/src/tool/bash.ts, validate that the resolved cwd actually exists before passing it to spawn(). If it doesn't exist:

  • Log a warning with both the requested and fallback paths
  • Fall back to Instance.directory

2. Improve error message (SECONDARY)

When spawn() fails with ENOENT due to a non-existent cwd, catch the error and provide a clear message: "Working directory does not exist: /path/to/dir" instead of the misleading posix_spawn error.

Quality Gates (Non-Negotiable)

  • TDD: Write tests before implementation
  • Coverage: 80%+ test coverage for new code
  • Linting: All code passes project linting rules
  • Local Verification: bun run typecheck and bun test pass
  • Fork manifest: Update .fork-features/manifest.json

Acceptance Criteria

  • Bash tool validates params.workdir exists before using it
  • Non-existent workdir falls back to Instance.directory with a logged warning
  • Error message for non-existent cwd is clear and actionable
  • Tests cover: valid workdir, non-existent workdir fallback, missing workdir (default behavior)
  • .fork-features/manifest.json updated with new feature entry

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions