Repository navigation
[Bug] Heredoc shebangs (!) still trigger history expansion → 300s timeout in **0.2.1-alpha.1** — independent repro, cross-verification, and one-line fix verified (follow-up to #3373 / #5046)
#9240
Replies: 2 comments
|
Independent confirmation from the newest tree, plus one source-level fact about the 300 s that your report observes but does not attribute — and the one link we could not close. Still unfixed after 0.2.1-alpha.1. On
const setup = ctx.terminals.startSend(owner, spawned.sessionId, {
text: 'stty -echo',
submit: true,
signal: combinedSignal,
})and a search for Why an idle-and-prompted shell still costs the full deadline. The completion loop is The settle route that would notice "the shell is back at its prompt with nothing running" without a marker is structurally unavailable on darwin:
isStdinWaiting(_pgid: number, _shellPid: number): boolean {
return false
}
The one link we could not close, and it is the interesting one. With the probe out, settlement has to come from the prompt-marker path ( Fix location confirmed unchanged, and still the right one. Regression fixture shape. Agreed, and it matches what we checked on the earlier threads in this family: the payload needs a |
|
Thanks for the independent repro and the verified one-line fix — the measurements here match what we see on For deployments that cannot patch upstream, this gap now has a plugin-form option, so the failure mode stops being a five-minute silence:
Mount it like any bundle patch — it ships - insert:
- id: histexpand-guard
name: '@argszero/dsh-histexpand-guard'What it does, and what it deliberately does not:
To be clear about the division of labour: this is a guard, not the fix. Refusing the shapes a rule can prove is strictly weaker than removing the hazard — your No runtime dependencies; verified against the |
Uh oh!
There was an error while loading. Please reload this page.
Summary
The persistent bash tool still mis-expands
!inside$'...'-wrapped commands on bash 3.2.57 (macOS default shell), causing the entire wrapped command — including the completion markers — to be discarded by history expansion before execution. The tool then waits for an end marker that will never appear, burns the full 300 s deadline, and resets the shell. The model receives only a timeout notice and no error output, so retries the same command and times out again.This is the same root cause already documented in #3373 (first report, on 0.1.0-rc.7) and #5046 (summary post, source-verified at commit
cd5ef81481, the 0.1.2-alpha.1 era). This post is an incremental confirmation: the bug is still present in 0.2.1-alpha.1 (the newest release, published 2026-10-03), across the entire published version line, together with an independent reproduction, a cross-verification matrix, and a working verification of the one-line fix already proposed in #5046.Environment
@deepseek-ai/dsh0.2.1-alpha.1 (dsh webhost)@deepseek-ai/dsh-tool-bash-persistent0.2.1-alpha.1,backendType: "shell"(defaults)@xterm/headless6.0.0/bin/bash3.2.57 (default), persistent shell spawned asbash --noprofile --norc -i,PS1='dsh> ', init sendstty -echo, cols=160/rows=40--noprofile --norc)Real-world impact (observed in a live session)
An agent session writing a ~200-line Python script to a file via a heredoc:
!(a 7,095-byte module, completed in 0.2 s).#!/usr/bin/env python3(7,059 bytes; three attempts — two near-identical retries, the third a shortened body behind a<<'EOF'delimiter): each attempt timed out after 300 s, and the file was never created.Your command timed out after 300 seconds or experienced an OOM error. Below is partial output:followed by theLOST_PREFIX_MESSAGEnote ("The beginning of this command output was dropped by the terminal scrollback limit") and a prompt residue — no trace of the actual bash error, so the model had no way to self-correct and retried identically.For agent workloads this is a silent, self-reinforcing failure: heredocs that emit scripts (the single most common pattern for writing code files) fail exactly when they carry a shebang, and the timeout framing makes the model believe the command is slow, not dead.
Minimal reproduction
In the persistent bash tool, run:
Observed: 300 s timeout,
/tmp/repro.pynever created, shell reset. Control: removing the shebang line makes the same command complete in <1 s.Root cause (independently confirmed; matches #5046)
No new mechanism is claimed here — this repro validates the chain already described in #5046 at the current version:
quoteForBash()encodes the model's multi-line command into a single physical line aseval -- $'...'with'→\'and\n→\n.$'...': the first\'inside the payload terminates the scanner's single-quote region, so everything after it is treated as unquoted.!in that region — most commonly the#!/usr/bin/env python3/#!/bin/bashfirst line of a heredoc body — is read as an event designator. With no matching history entry, bash printsbash: !/usr/bin/env: event not foundand discards the entire physical line.The discard explains the symptom signature: the
printffor the start marker is part of the same discarded line, so neither__DSH_PERSISTENT_BASH_START_*__nor the end marker ever reaches the terminal.commandOutput()/partialOutput()then find no start marker at all, which is exactly why the model-visible partial output carries theLOST_PREFIX_MESSAGE.Secondary behavior worth noting for recovery logic
Reproducing outside dsh (node-pty PTY, faithful re-implementation of the exact spawn/env/init/wrap sequence from 0.2.1-alpha.1) shows:
bash: !/usr/bin/env: event not foundand returns to thedsh>prompt within milliseconds of the line being written; there is no running child at that point.This supports the recovery taxonomy sketched in #5046: a missing end marker means "command not confirmed", not "a foreground child is still running". On the history-expansion path specifically, the shell is idle-and-prompted while the tool keeps polling, which also explains why the reset-and-retry loop cannot self-heal.
Cross-verification matrix
Faithful PTY reproduction of the 0.2.1-alpha.1 execution path (same spawn args,
PS1/PROMPT_COMMANDenv,stty -echoinit, samequoteForBash/wrapCommand/marker logic, one-shotwrite(wrapped + "\r")):bash: !/usr/bin/env: event not found; no start marker ever emitted!, 7,522 chars)#!/usr/bin/env python3first lineevent not foundset +HRows 3–4 isolate the shebang's
!as the sole trigger; rows 1/2/6 confirm the fix.Version audit (npm tarballs)
quoteForBashhas been present since the first published release, and no published version disables history expansion in the init send:@deepseek-ai/dsh-tool-bash-persistentset +Hpresentsttyinit yet;quoteForBashalready present)stty -echocd5ef81481)stty -echostty -echostty -echostty -echoSo the issue is not a regression of the 0.2.x line — it has simply never been fixed since the first release, while the affected user base (macOS default bash) hits it routinely whenever an agent writes a script file with a shebang.
Fix: one line in the init send (as already scoped in #5046), verified
The fix location agreed in #5046 — keep it in the awaited, tool-scoped init send rather than in
DEFAULT_BASH_ARGS— is what I patched locally and verified:After this change, the exact command that previously hung for 300 s completes in ~2.2 s with normal start/end markers and exit code 0 (matrix row 6). An agent shell has no use for
!history designators, so defaulting history expansion off for this PTY is behavior-preserving for every legitimate tool call (row 2 and all non-!commands unaffected).For a regression fixture (per the shape noted in #5046): the test command must contain a
\'in the payload before the!— e.g. a quoted string followed by a shebang line inside a heredoc — otherwise the quote scanner stays inside$'...'and the!is not mis-expanded; e.g. on bash 5.x the same test should simply pass.One caveat for the fix's blast radius, already discussed in #5046:
set +Honly affects the persistent interactive shell of this tool. It does not change behavior for non-persistent/sandboxed bash paths (which are non-interactive and don't expand history anyway), and it does not alterHISTSIZE/history recording (arrows/up-history via readline keep working — only!designator expansion is disabled).References
!, 0.1.0-rc.7, macOS 300 s timeout)echo '#!/bin/bash' > fvariant; single-quote-before-!patternprintf '%s\n' 'Hello, World!'hangs the same way!inside long single-quoted Swift one-liner hangs the bash toolAll reactions