shareAI-lab/learn-claude-code · error · ValueError
Bash command cannot be empty
Error message
Bash command cannot be empty
What it means
Raised by BackgroundManager.start() in s11_background_tasks/code.py:319 when the bash block's 'command' input is missing, not a string, or empty after stripping. The background thread will shell out with this exact string, so a blank command would spawn a no-op shell with nothing to capture. The check runs after the 'Only Bash' name check and before the task is registered and the thread starts.
Source
Thrown at s11_background_tasks/code.py:319
return str(output)
# -- New in s11: background execution --
class BackgroundManager:
def __init__(self):
self.tasks: dict[str, dict] = {}
self.results: dict[str, str] = {}
self._ready: list[str] = []
self._counter = 0
self._lock = threading.Lock()
def start(self, block) -> str:
if block.name != "bash":
raise ValueError("Only Bash commands can run in the background")
command = block.input.get("command")
if not isinstance(command, str) or not command.strip():
raise ValueError("Bash command cannot be empty")
with self._lock:
self._counter += 1
task_id = f"bg_{self._counter:04d}"
self.tasks[task_id] = {
"tool_use_id": block.id,
"command": command,
"status": "running",
}
thread = threading.Thread(
target=self._run,
args=(task_id, command),
daemon=True,
)
try:
thread.start()
except Exception:View on GitHub (pinned to 985456f4ad)
Solutions
- Validate block.input.get('command') is a non-empty string before calling start().
- When the command comes from an LLM, reject/regenerate tool calls whose arguments fail a schema check rather than forwarding them.
- Fix template producers so an empty command is never emitted — require the variable and fail early with a clear message.
Example fix
# before
bg_id = background.start(block)
# after
command = block.input.get('command') if isinstance(block.input, dict) else None
if isinstance(command, str) and command.strip():
bg_id = background.start(block)
else:
raise ValueError('background start requires a non-empty bash command') Defensive patterns
Strategy: validation
Validate before calling
def block_has_command(block) -> bool:
cmd = block.input.get('command') if isinstance(getattr(block, 'input', None), dict) else None
return isinstance(cmd, str) and bool(cmd.strip()) Type guard
def is_runnable_bash_block(block) -> bool:
return (getattr(block, 'name', '') == 'bash'
and isinstance(block.input, dict)
and isinstance(block.input.get('command'), str)
and bool(block.input['command'].strip())) Try / catch
try:
bg_id = background.start(block)
except ValueError as e:
if 'cannot be empty' in str(e):
ask_model_to_regenerate_tool_call(block) # or surface to the user
else:
raise Prevention
- Schema-check LLM tool_use arguments before dispatching to background.
- Require the command field in templates; fail early on empty interpolation.
- Test background dispatch with malformed blocks (missing key, list instead of string).
When it happens
Trigger: Passing a block whose input is {} (no 'command' key); input {'command': ''} or {'command': ' '}; input {'command': ['ls', '-l']} (a list, not a string); input built from an LLM tool call where the command argument was omitted or nulled.
Common situations: Malformed agent tool_use blocks from the model (missing arguments); templates that interpolate a possibly-empty variable into the command; refactors that changed the input schema key from 'command' to something else; UI 'run in background' submitted from an empty input field.
Related errors
- Memory name cannot be empty
- Memory description and body cannot be empty
- Task subject cannot be empty
- Only Bash commands can run in the background
- background result cannot be empty
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/f6ff4b9383e0264d.
Report an issue: GitHub.