Repository navigation
Fix heredoc commands hanging due to indentation of terminator - #446
Conversation
buildCommandBlock() passed the entire user command through indentLines(), which added space prefixes to every line including heredoc terminators. Bash requires terminators at column 0, so the indented terminator was never matched, hanging the command and locking the session mutex permanently. Indent each part of the command block independently, applying indentation only to the first line of the user command and leaving subsequent lines verbatim.
🦋 Changeset detectedLatest commit: eabd323 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
OpenCode Review
Good fix! This correctly addresses the heredoc hanging issue by only indenting the first line of user commands, preserving bash heredoc terminator requirements. The solution is surgical and well-tested.
The changeset properly explains the user-facing benefit and the comprehensive test coverage demonstrates the fix works for various heredoc scenarios including expansion and subsequent command execution.
Looks good to merge.
|
/bonk review this PR |
|
Now I have the full picture. Let me write the review. PR Review: Fix heredoc commands hanging due to indentation of terminator (#446)Problem AnalysisThe diagnosis is correct and well-explained. Fix AnalysisThe fix is sound. Instead of indent-then-join, it's join-already-indented-parts:
This preserves heredoc terminators, heredoc body content, and any other multi-line construct the user might pass. IssuesOne correctness concern with No issue with the heredoc body content: Lines after the first in a heredoc are literal content fed to the command's stdin. Not indenting them is correct -- the user's content should be delivered as-is. Edge cases to consider
TestsThe 4 new tests are well-chosen:
One test that would strengthen coverage: a multi-line non-heredoc command (e.g., ChangesetThe changeset correctly references VerdictThe fix is correct, minimal, and well-tested. The root cause analysis is accurate, the solution is surgical (only changes how Looks good to merge. |
commit: |
🐳 Docker Images PublishedDefault: FROM cloudflare/sandbox:0.0.0-pr-446-a695807With Python: FROM cloudflare/sandbox:0.0.0-pr-446-a695807-pythonWith OpenCode: FROM cloudflare/sandbox:0.0.0-pr-446-a695807-opencodeVersion: Use the 📦 Standalone BinaryFor arbitrary Dockerfiles: COPY --from=cloudflare/sandbox:0.0.0-pr-446-a695807 /container-server/sandbox /sandbox
ENTRYPOINT ["/sandbox"]Download via GitHub CLI: gh run download 22646179935 -n sandbox-binaryExtract from Docker: docker run --rm cloudflare/sandbox:0.0.0-pr-446-a695807 cat /container-server/sandbox > sandbox && chmod +x sandbox |
Summary
buildCommandBlock()indenting all lines of the user command — including heredoc terminators — which caused bash to never find the terminator, hanging the command forever and locking the session mutex for all subsequent commands.Problem
exec("cat << 'EOF'\nhello\nEOF")hangs permanently. ThebuildCommandBlock()function insession.tsjoins the user command with shell scaffolding (env setup, exit code capture) and passes everything throughindentLines(), which prepends spaces to every non-empty line. This transforms:into:
Bash requires heredoc terminators at column 0 (or tab-indented with
<<-), so the indentedEOFis never matched. The command blocks forever waiting for input, the exit code file is never written, andwaitForExitCode()never resolves. SinceSessionManager.executeInSession()holds the per-session mutex for the entire command duration, the mutex is never released and all subsequent commands to that session queue indefinitely.Fix
Changed
buildCommandBlock()to indent each part independently instead of indenting the joined result. The user command gets indentation only on its first line — subsequent lines (heredoc body, terminators) pass through verbatim. The env setup/cleanup blocks and exit code capture are generated code and remain safely indented.Reviewer notes
indentLineshelper is unchanged — the fix is entirely in howbuildCommandBlockuses it.indent + 2matches the original" " + commandprefix run throughindentLines).buildCommandBlock(lines 915, 943, 975, 986) are unchanged — they still receive a correctly indented block.