[BUG] Claude Code sandbox Bash tool loses pipe data when pipeline is last element

Resolved 💬 16 comments Opened Jan 5, 2026 by archer-eric Closed Mar 31, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Summary

When Claude Code's Bash tool runs commands in sandbox mode and a pipeline is the final element in the command list, data (stdout and stderr) is not passed through | or |& pipes to subsequent commands. It is completely lost; it does not reach the next command and does not appear in tool output.

Sink commands still execute and their own output is captured: seq 2 | wc outputs 0 0 0, showing wc ran but received nothing.

A simple workaround is to append a trailing semicolon: seq 2 | cat;

Environment

  • Claude Code 2.0.76
  • Claude Opus 4.5
  • Ubuntu 24.04.3 LTS
  • Linux 6.14.0-36-generic
  • Bash 5.2.21
  • Minimal Claude Code config (sandbox enable only):
{
  "sandbox": {
    "enabled": true
  }
}

Steps to Reproduce

Run Claude Code with the above config on an appropriate system and ask it to run commands in the sandbox. For example:

`````bash
claude --print 'Use the Bash tool to run each of these commands in the sandbox (one at a time) and output the exact results from the tool:

seq 2 | cat
seq 2 | cat;
seq 2 | wc
seq 2 | wc;

Sample output format:

  1. first command

output of first command (Bash tool message if none)

  1. second command

...
'

`````

Sample (nondeterministic) output:

`````

Results

  1. seq 2 | cat

Tool ran without output or errors

  1. seq 2 | cat;
1
2
  1. seq 2 | wc
      0       0       0
  1. seq 2 | wc;
      2       2       4

Interesting finding: There's a notable difference in behavior between commands with and without a trailing semicolon. The commands without semicolons appear to have issues with the pipe - cat produces no output and wc reports 0 lines/words/bytes. Adding a semicolon at the end causes the commands to work correctly. This suggests something unusual about how the sandbox handles pipes at the end of commands.

`````

Behavior Pattern

Fails (pipeline is final element in list)

These commands do not have any output:

seq 2 | cat                         # basic
seq 2 | cat | cat                   # multi-stage
echo start; seq 2 | cat             # outputs "start"; pipeline loses data
echo start && seq 2 | cat           # outputs "start"; pipeline loses data
(seq 2) | cat                       # subshell as source
{ seq 2; } | cat                    # brace group as source
yes | head -1                       # SIGPIPE case
shopt -s lastpipe; seq 2 | cat      # lastpipe doesn't help

Works (pipeline wrapped or followed)

seq 2 | cat;                        # trailing semicolon
seq 2 | cat; true                   # any trailing command
(seq 2 | cat)                       # wrapped in subshell
{ seq 2 | cat; }                    # wrapped in braces
bash -c "seq 2 | cat"               # nested shell
echo "$(seq 2 | cat)"               # command substitution
seq 2 | cat & wait                  # background + wait (completion message shown)
seq 2 | cat # comment               # trailing comment
seq 2 | cat #                       # trailing comment character

Works (source uses redirection)

cat <<< "1" | cat                   # here-string
cat <<EOF | cat                     # here-doc
1
EOF
cat < <(seq 2) | cat                # process substitution with redirect
cat <(seq 2)                        # process substitution (not a pipeline)
seq 2 | cat > /tmp/x; cat /tmp/x    # output redirection works

stderr also affected

(echo "test" >&2) |& wc             # outputs "0 0 0" — wc received nothing
(echo "test" >&2) |& wc;            # outputs "1 1 5" — works with semicolon

⚠️ /dev/stdin may cause hangs

seq 2 | cat < /dev/stdin            # outputs 1,2 but may hang Claude Code afterward

(Separate bug being reported)

Technical Notes

Data does not flow in multi-stage pipelines without workaround:

rm -f /tmp/claude/test.txt
seq 2 | tee /tmp/claude/test.txt | cat    # no output; file is 0 bytes
seq 2 | tee /tmp/claude/test.txt | cat;   # outputs 1,2; file is 4 bytes

_[Created in cooperation with Claude Code using Claude Opus 4.5]_

What Should Happen?

see report above

Error Messages/Logs

Steps to Reproduce

see report above

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.0.76 (Claude Code)

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Other

Additional Information

_No response_

View original on GitHub ↗

16 Comments

github-actions[bot] · 6 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/8318
  2. https://github.com/anthropics/claude-code/issues/11225
  3. https://github.com/anthropics/claude-code/issues/15599

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

archer-eric · 6 months ago

Note: This is an annoying problem, as Claude Code likes to run things like

commmand-with-long-output | head -20

This exits with no output and no error in the sandbox, and Claude Code assumes that command-with-long-output had no output, leading it down bad logic paths.

archer-eric · 6 months ago

I have verified that I am not the only one seeing this bug. A coworker sees the same problem on a Mac. Is nobody else using Claude Code sandboxes?

archer-eric · 6 months ago

workaround: I added this to the end of my ~/.claude/CLAUDE.md file:

## ALERT!

- There is currently a bug in Claude Code Shell tool when commmands are run in a sandbox: Pipes do not work correctly unless you append a ";" to the end of the entire command! Yes, it's strange, but we need to ensure that we always add ";" to the end of all commands when running in a sandbox until this bug is fixed.

With this, Claude Code (Opus 4.5) consistently adds " ;" to the end of every Bash tool command when running in the sandbox (even ones without pipes, which is fine).

archer-eric · 6 months ago

Still a problem in Claude Code v2.1.2:

 ▐▛███▜▌   Claude Code v2.1.2
▝▜█████▛▘  Opus 4.5 · Claude Max
  ▘▘ ▝▝    /home/[ELIDED]

❯ run in the sandbox with no trailing semicolon: echo hi | cat 

● Bash(echo hi | cat)
  ⎿  (No content)

● The command ran but produced no output. This demonstrates the pipe bug mentioned in your CLAUDE.md - without the trailing semicolon, the piped output doesn't appear.

❯ now try it with the trailing semicolon 

● Bash(echo hi | cat;)
  ⎿  hi

● With the trailing semicolon, it works correctly and outputs hi. The workaround is confirmed.
archer-eric · 5 months ago

Still a problem in Claude Code 2.1.17 on Ubuntu 24.04. This makes Claude Code with sandbox unreliable on Linux, as Claude Code is constantly piping commands to head and tail, and this bug prevents any output from showing up.

How does one get Anthropic's attention among the thousands of issues being reported?

`````
$ claude --print 'Use the Bash tool to run each of these commands in the sandbox (one at a time) and output the exact results from the tool:

seq 2 | cat
seq 2 | cat;
seq 2 | wc
seq 2 | wc;

Sample output format:

  1. first command

output of first command (Bash tool message if none)

  1. second command

...
'

`````

Here are the results: 1. seq 2 | cat (Tool ran without output or errors) 2. seq 2 | cat; `` 1 2 ` 3. seq 2 | wc ` 0 0 0 ` 4. seq 2 | wc; ` 2 2 4 `` This confirms the sandbox pipe bug mentioned in CLAUDE.md. Without the trailing semicolon, pipes don't work correctly - either producing no output or incorrect results (0 lines/words/bytes). With the semicolon, the commands work as expected.
tsukumijima · 5 months ago

I'm having a similar issue and have been forced to disable the sandbox because it interferes with operation.
When developing, I prefer to access my development environment on my Ubuntu 22.04 LTS development machine via SSH from macOS, but the Linux sandbox may not be maintained.

archer-eric · 5 months ago

Still a problem in Claude Code v2.1.29 on Linux using a sandbox.

The silent failure giving Claude Code misinformation has to be causing some really bad results for folks trying the sandbox on Linux, possibly causing them to lose respect for Claude Code, if not causing actual harm.

marcindulak · 5 months ago
  1. "How does one get Anthropic's attention among the thousands of issues being reported?" The official way is through https://github.com/anthropics/claude-code/issues/21953
  2. I don't want to derail the topic of this issue, but how is the sandbox usable for you in general on Ubuntu 24.04 https://github.com/anthropics/claude-code/issues/14719?
  3. I don't reproduce your example in 10 tries on /etc/os-release:PRETTY_NAME="AlmaLinux 9.7 (Moss Jungle Cat)" with 2.1.17 (Claude Code), I also needed to add --dangerously-skip-permissions. I believe we may also need export DISABLE_PROMPT_CACHING=1

Your instructions:

Here are the results:

1. `seq 2 | cat`
Error: This Bash command contains multiple operations. The following part requires approval: seq 2

2. `seq 2 | cat;`
Error: This Bash command contains multiple operations. The following part requires approval: seq 2

3. `seq 2 | wc`
Error: This Bash command contains multiple operations. The following part requires approval: seq 2

4. `seq 2 | wc;`
Error: This Bash command contains multiple operations. The following part requires approval: seq 2

All four commands returned the same error about requiring approval for multiple operations.

````
rm -rf /tmp/test
cd /tmp/test
export DISABLE_PROMPT_CACHING=1

claude --dangerously-skip-permissions --print 'Use the Bash tool to run each of these commands in the sandbox (one at a time) and output the exact results from the tool:

seq 2 | cat
seq 2 | cat;
seq 2 | wc
seq 2 | wc;

Sample output format:

  1. first command

output of first command (Bash tool message if none)

  1. second command

...
'

````

````
Here are the results:

  1. seq 2 | cat
1
2
  1. seq 2 | cat;
1
2
  1. seq 2 | wc
      2       2       4
  1. seq 2 | wc;
      2       2       4

The trailing semicolon has no effect on the output in any of these cases.

````

bash --version
GNU bash, version 5.1.8(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2020 Free Software Foundation, Inc.
...
archer-eric · 5 months ago

@marcindulak

  1. Thanks for the tip.
  2. My current problems with sandbox on Ubuntu 24.04 are (especially the first):
  1. Thanks for trying. Neither export DISABLE_PROMPT_CACHING=1 nor --dangerously-skip-permissions improves my results with the current issue.
marcindulak · 5 months ago

When I mentioned the need for --dangerously-skip-permissions is because without it, your test case was blocked (returned Error: This Bash command contains multiple operations. The following part requires approval) , so some of our settings may differ.

archer-eric · 5 months ago

@marcindulak Ah yes, I probably allow the example commands like seq and cat and wc.

djmmoss · 4 months ago

Additional root cause detail: The --unshare-pid flag in bwrap is what makes this race nearly deterministic inside the sandbox vs. intermittent elsewhere.

When bash exec()s into the last pipeline stage (an optimization bash uses when there are no more commands after the pipeline), that process becomes PID 1 in the bwrap PID namespace. When PID 1 exits in a PID namespace, the kernel immediately tears down the entire namespace and force-exits bwrap — all nearly atomically from the perspective of the outer process.

Node.js/libuv sees the bwrap process-exit event and the pipe EOF arrive simultaneously. If the tool finalizes output on the exit event (process ended) rather than the close event (stdio fully drained), the last write is lost every time.

Outside the sandbox with no bwrap/PID namespace, the race window is small enough that it often resolves correctly. Inside the sandbox, the cascading teardown (tail exits → PID 1 exits → kernel destroys namespace → bwrap exits) makes it deterministic.

The fix would be to await the close event on the stdout stream (which only fires after the pipe is fully drained) rather than the exit event on the process, before returning the tool result. This is a well-known Node.js subprocess pitfall.

Verified on Claude Code 2.1.63, Linux 6.14.0-35-generic, bwrap with --unshare-pid.

github-actions[bot] · 3 months ago

Closing for now — inactive for too long. Please open a new issue if this is still relevant.

archer-eric · 3 months ago

Looks like this was fixed for me, somewhere before Claude Code v2.1.87

github-actions[bot] · 3 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.