shareAI-lab/learn-claude-code · error · ValueError
only bash can run in the background
Error message
only bash can run in the background
What it means
start_background_task() executes a tool block in a worker thread, but only the 'bash' tool is eligible: any other tool name (read, edit, mcp__*, ...) raises this inside the worker, so the background entry is marked failed with 'Error: ValueError: only bash can run in the background'. Long-running work is exclusively shell commands by design.
Source
Thrown at s15_integrated_harness/code.py:2108
def should_run_background(tool_name: str, tool_input: dict) -> bool:
return (
tool_name == "bash"
and tool_input.get("run_in_background") is True
)
def start_background_task(block, handlers: dict) -> str:
global _bg_counter
_bg_counter += 1
bg_id = f"bg_{_bg_counter:04d}"
command = block.input.get("command", block.name)
cwd, cwd_error = _agent_cwd()
def worker():
try:
if block.name != "bash":
raise ValueError("only bash can run in the background")
if cwd_error:
raise ValueError(cwd_error.removeprefix("Error: "))
output, exit_code = _run_bash_process(
str(block.input["command"]), cwd)
result = _format_bash_result(output, exit_code)
status = "completed" if exit_code == 0 else "failed"
except Exception as exc:
result = f"Error: {type(exc).__name__}: {exc}"
status = "failed"
trigger_hooks("PostToolUse", block, result)
with background_lock:
background_tasks[bg_id]["status"] = status
background_results[bg_id] = str(result)
with background_lock:
background_tasks[bg_id] = {
"tool_use_id": block.id,
"command": command,View on GitHub (pinned to 985456f4ad)
Solutions
- Run only bash commands in the background; run other tools synchronously.
- For slow non-bash operations, wrap them in a shell command (e.g. curl) if policy permits.
- Gate the background option on block.name == 'bash' in the caller before invoking start_background_task.
Example fix
// before
start_background_task(read_block, handlers) // block.name == 'read'
// after
if block.name == "bash":
start_background_task(block, handlers)
else:
run_sync(block, handlers) Defensive patterns
Strategy: validation
Validate before calling
def can_background(block) -> bool:
return block.name == "bash" Type guard
def is_bash_block(block) -> bool:
return getattr(block, "name", None) == "bash" Prevention
- Only request background execution for bash blocks.
- Gate the background flag on tool name in your wrapper.
- For slow non-bash ops, run synchronously or wrap in a shell command.
When it happens
Trigger: Requesting background execution for a non-bash block — e.g. marking a read_file or MCP tool call as background/async; a scheduler that routes every slow tool through start_background_task; block.name mutated between validation and dispatch.
Common situations: Agents trying to background a slow MCP fetch; wrappers that set a background flag generically on all tool calls; version change where the flag started applying to more tools.
Related errors
- Only Bash commands can run in the background
- Max 20 todos allowed
- Item {item_id}: text required
- Item {item_id}: invalid status '{status}'
- Only one task can be in_progress at a time
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/0b8abbcfc2b74b26.
Report an issue: GitHub.