Skip to content

fix(sdk): keep stdin open until background tasks finish - #1532

Open
JamesScrase-PortSwigger wants to merge 1 commit into
anthropics:mainfrom
JamesScrase-PortSwigger:keep-stdin-open-bg-tasks
Open

fix(sdk): keep stdin open until background tasks finish#1532
JamesScrase-PortSwigger wants to merge 1 commit into
anthropics:mainfrom
JamesScrase-PortSwigger:keep-stdin-open-bg-tasks

Conversation

@JamesScrase-PortSwigger

Copy link
Copy Markdown

Problem

When the model launches a background Bash task (run_in_background: true, or a timeout auto-background) and ends its turn, the background shell is terminated shortly after the final result — the termination fires once stdin has closed, and runClaudeWithSdk passes a string prompt, so stdin closes immediately. A long background command (build, test suite, …) never finishes and its output is never consumed, yet the step still reports subtype: "success". See #1462 for the user-facing "green run, nothing happened" symptom.

Change

base-action/src/run-claude-sdk.ts: feed the prompt through an async generator that yields it and then holds the CLI's stdin open, and break the loop only when a result reports no live background tasks (tracked via the background_tasks_changed system message). A finally releases the hold if the stream ends or throws.

Because stdin stays open until nothing is live, a completed or killed background task keeps the process alive long enough to run to completion / be observed, and the model receives its follow-up turn(s) to consume the output. Runs with no background tasks are unaffected — they break on the first result, preserving the anti-hang guard from #1339.

Possible alternatives

This behavior originates in Claude Code / the Agents SDK, not in claude-code-action; this PR is a consumer-side mitigation. You may prefer a holistic upstream fix — e.g. not terminating background shells while an SDK consumer is still iterating the stream. Happy to adapt or close this in favor of that, or it may be preferred to make this a config option in case other users still desire long-running tasks to be killed or have a timeout.

See also

#1500 fixes this just for background subagents, but doesn't work for backgrounded Bash commands.
Fixes #1531, #1499.

Environment

  • @anthropic-ai/claude-agent-sdk ^0.3.211
  • Headless / -p (GitHub Actions)

In headless runs the CLI terminates a background Bash task shortly after the
final result once stdin has closed. runClaudeWithSdk passes a string /
one-shot prompt, so stdin closes immediately and a background command
launched during a turn is killed before it finishes — while the step still
reports success and its output is never consumed.

Feed the prompt as a stream and keep stdin open until a result reports no
live background tasks (tracked via background_tasks_changed), so background
work runs to completion and the model receives its follow-up turn(s). Because
stdin stays open until then, background work started in a later turn is
covered too, not just the first. The no-background path is unchanged (break
on the first result with an empty live set), preserving the anti-hang guard
from anthropics#1339; keeping the loop open while tasks are live also covers the
background-subagent case (anthropics#1499). A run whose background work never settles
is bounded by the job's timeout-minutes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Headless runs terminate background Bash tasks shortly after the turn ends

1 participant