Skip to content

Bash tool cwd is silently ignored when the command starts with cmd & #2890

Description

@shining-mind

What version of Kimi Code is running?

0.35.0

Which open platform/subscription were you using?

Kimi Code (OAuth)

Which model were you using?

K3

What platform is your computer?

Darwin 25.3.0 arm64 arm

What issue are you seeing?

TL;DR

The Bash tool applies its cwd parameter by textually prepending cd '' && to the user's command. If the command's first line ends with &, shell operator precedence (&& binds tighter than &) sends the entire chain — including the cd — into a background subshell. The main shell never changes directory, so all following lines of the script run in the wrong directory. Nothing is reported; the cwd parameter is silently ignored.


What the caller expects

 // Bash tool call                                                                                                     
 {                                                                                                                     
   "cwd": "/repo/packages/ui",                                                                                         
   "command": "node server.js &\nsleep 1\nnode check.js"                                                               
 }                                                                                                                     

The caller's mental model: the whole script runs with /repo/packages/ui as its working directory — server.js in the background, check.js in the foreground, both in that directory.

What actually runs

The harness prepends the cd, producing:

 cd '/repo/packages/ui' && node server.js &                                                                            
 sleep 1                                                                                                               
 node check.js                                                                                                         

Bash parses line 1 as "background the whole cd ... && node server.js list". The cd happens inside the background subshell and has no effect on the main shell. So:

  • node server.js — background, correct directory (output even looks right, e.g. it prints paths from the requested cwd);
  • node check.js — foreground, original default directory (e.g. the repo root).

The asymmetry is the nasty part: the visible output suggests everything worked, while the foreground half of the script silently ran somewhere else. In practice this surfaced as Cannot find module errors with a require stack pointing at a completely different directory tree than the requested cwd, plus orphaned background processes the script's own kill %1 couldn't reach.

Evidence from a real session wire log

Anonymized excerpt from wire.jsonl — the tool call and its result (paths shortened):

  // tool.call (Bash, step 81)                                                                                          
  {                                                                                                                     
    "cwd": "/repo/.worktrees/feature-x/frontend/packages/ui",                                                           
    "command": "node scripts/serve-static.mjs storybook-static 6175 &\nsleep 1\nnode -e                                 
\"require('@playwright/test')...\""                                                                                     
  }                                                                                                                     
                                                                                                                        
  // tool.result — note the require stack: the repo ROOT, not the requested cwd                                         
  "Error: Cannot find module '@playwright/test'                                                                         
  Require stack:                                                                                                        
  - /repo/[eval]"                                                                                                       

The backgrounded server correctly started from /repo/.worktrees/feature-x/... (it inherited the cd), while the foreground node -e resolved modules from /repo — the harness's default cwd. The requested cwd never applied to it.

What steps can reproduce the bug?

  mkdir -p /tmp/kimi-cwd-repro                                                                                          

Then, via the Bash tool with cwd set to /tmp/kimi-cwd-repro:

  sleep 0.1 & echo "foreground cwd: $(pwd)"; wait                                                                       

Expected: foreground cwd: /tmp/kimi-cwd-repro
Actual: the foreground pwd prints the caller's original working directory.

What is the expected behavior?

No response

Additional information

Suggested fix

Apply cwd as a real working directory of the spawned process (e.g. spawn(shell, { cwd })) instead of string-prepending cd. The cwd parameter is part of the tool's contract and should hold regardless of what shell syntax the command contains (&, ||, subshells, etc.).

If that's not feasible, a stopgap would be to detect commands whose first line ends with & and warn or reject, steering the caller to the tool's native run_in_background mechanism.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions