# Objective This is a follow-up to my own PR #59270 which fixed the hang it targeted (#55042) only partially and broke `ShellBuilder::build` for fish. **The hang is only half fixed.** The agent's terminal tool wraps commands as `sh -i -c 'exec </dev/null; <command>'`. The redirect sits on the same line as the command, but POSIX shells parse an entire line before executing any of it. So when `<command>` has a syntax error, the whole line is discarded — including the `exec` that was supposed to close stdin. Interactive `dash` then recovers from the error, finds stdin still attached to the PTY, and sits at a `$` prompt until the user presses Ctrl+D. That means #59270 only helped when the redirect had already run, i.e. multi-line commands whose error is on a later line. A single-line command still hangs, including `cat <(echo hi)`, the example from that PR's own description. Probably prompts or tool definitions changed since that PR landed which now make it more likely again to run into this issue, at least with Anthropic models. **fish runs no command at all.** #59270 also applied the `exec </dev/null; …` form to fish, replacing its `begin; …; end </dev/null` wrapper. But fish's `exec` requires a command to replace the shell with, so a bare `exec </dev/null` is a usage error: fish prints `exec`'s help text, skips the rest of the command, and exits 0. ## Solution For POSIX shells, put the redirect on its own line: ```sh sh -i -c 'exec </dev/null <command>' ``` That makes it a separate parse-and-execute unit, so it runs before the command line is parsed. Any syntax error is then reported by a shell that already has `/dev/null` on stdin, and it exits instead of prompting. For fish, go back to `begin; <command>; end </dev/null`. fish exits on a parse error even when interactive, so it never needed the `exec` form to begin with. Only the `Posix` and `Fish` arms change; the other shells' redirects are untouched. Note this is still a targeted fix for `dash`. The underlying reason a syntax error becomes an interactive prompt is that agent terminals are built with `-i` at all — non-interactive `dash` exits cleanly even with no redirect. Dropping `-i` would remove the whole class of hang, but it would also stop sourcing users' interactive shell config in agent terminals. ## Testing `cargo test -p util --lib shell_builder` covers the changed command assembly. Reviewers can check the shell behavior directly on a PTY, without building Zed: ```sh # hangs at a `$` prompt until Ctrl+D script -qec "sh -i -c 'exec </dev/null; echo hi ;;'" /dev/null # exits immediately with just the syntax error script -qec "sh -i -c 'exec </dev/null echo hi ;;'" /dev/null ``` ```sh fish -i -c 'exec </dev/null; echo test' # prints exec's help, no "test", exit 0 fish -i -c 'begin; echo test; end </dev/null' # prints "test" fish -i -c 'if true' # exit 127, no hang ``` macOS is unaffected in practice because `/bin/sh` there is bash, which exits on a syntax error even with `-i`; Windows goes through the `Cmd`/`PowerShell` arms, which this doesn't touch. A `zsh`-as-`/bin/sh` setup is likewise unaffected. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed agent terminal commands hanging at a shell prompt after a syntax error |
||
|---|---|---|
| .agents/skills | ||
| .cargo | ||
| .cloudflare | ||
| .config | ||
| .factory | ||
| .github | ||
| .wezel | ||
| .zed | ||
| assets | ||
| ci | ||
| crates | ||
| docs | ||
| extensions | ||
| legal | ||
| nix | ||
| script | ||
| tooling | ||
| .git-blame-ignore-revs | ||
| .gitattributes | ||
| .gitignore | ||
| .mailmap | ||
| .prettierrc | ||
| .rules | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| clippy.toml | ||
| CODE_OF_CONDUCT.md | ||
| compose.yml | ||
| CONTRIBUTING.md | ||
| debug.plist | ||
| default.nix | ||
| Dockerfile-collab | ||
| Dockerfile-collab.dockerignore | ||
| Dockerfile-cross.dockerignore | ||
| Dockerfile-distros | ||
| Dockerfile-distros.dockerignore | ||
| flake.lock | ||
| flake.nix | ||
| GEMINI.md | ||
| LICENSE-APACHE | ||
| LICENSE-GPL | ||
| livekit.yaml | ||
| lychee.toml | ||
| Procfile | ||
| Procfile.web | ||
| README.md | ||
| renovate.json | ||
| REVIEWERS.conl | ||
| rust-toolchain.toml | ||
| rustfmt.toml | ||
| shell.nix | ||
| typos.toml | ||
Zed
Welcome to Zed, a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.
Installation
On macOS, Linux, and Windows you can download Zed directly or install Zed via your local package manager (macOS/Linux/Windows).
Other platforms are not yet available:
- Web (tracking discussion)
Developing Zed
Contributing
See CONTRIBUTING.md for ways you can contribute to Zed.
Also... we're hiring! Check out our jobs page for open roles.
Licensing
Zed source code is licensed primarily under GPL-3.0-or-later, with Apache-2.0 components where marked.
License information for third party dependencies must be correctly provided for CI to pass.
We use cargo-about to automatically comply with open source licenses. If CI is failing, check the following:
- Is it showing a
no license specifiederror for a crate you've created? If so, addpublish = falseunder[package]in your crate's Cargo.toml. - Is the error
failed to satisfy license requirementsfor a dependency? If so, first determine what license the project has and whether this system is sufficient to comply with this license's requirements. If you're unsure, ask a lawyer. Once you've verified that this system is acceptable add the license's SPDX identifier to theacceptedarray inscript/licenses/zed-licenses.toml. - Is
cargo-aboutunable to find the license for a dependency? If so, add a clarification field at the end ofscript/licenses/zed-licenses.toml, as specified in the cargo-about book.
Sponsorship
Zed is developed by Zed Industries, Inc., a for-profit company.
If you’d like to financially support the project, you can do so via GitHub Sponsors. Sponsorships go directly to Zed Industries and are used as general company revenue. There are no perks or entitlements associated with sponsorship.