{"record":{"id":"f6ff4b9383e0264d","repo":"shareAI-lab/learn-claude-code","slug":"bash-command-cannot-be-empty","errorCode":null,"errorMessage":"Bash command cannot be empty","messagePattern":"Bash command cannot be empty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"s11_background_tasks/code.py","lineNumber":319,"sourceCode":"    return str(output)\n\n\n# -- New in s11: background execution --\n\nclass BackgroundManager:\n    def __init__(self):\n        self.tasks: dict[str, dict] = {}\n        self.results: dict[str, str] = {}\n        self._ready: list[str] = []\n        self._counter = 0\n        self._lock = threading.Lock()\n\n    def start(self, block) -> str:\n        if block.name != \"bash\":\n            raise ValueError(\"Only Bash commands can run in the background\")\n        command = block.input.get(\"command\")\n        if not isinstance(command, str) or not command.strip():\n            raise ValueError(\"Bash command cannot be empty\")\n\n        with self._lock:\n            self._counter += 1\n            task_id = f\"bg_{self._counter:04d}\"\n            self.tasks[task_id] = {\n                \"tool_use_id\": block.id,\n                \"command\": command,\n                \"status\": \"running\",\n            }\n\n        thread = threading.Thread(\n            target=self._run,\n            args=(task_id, command),\n            daemon=True,\n        )\n        try:\n            thread.start()\n        except Exception:","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s11_background_tasks/code.py#L301-L337","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nbg_id = background.start(block)\n\n# after\ncommand = block.input.get('command') if isinstance(block.input, dict) else None\nif isinstance(command, str) and command.strip():\n    bg_id = background.start(block)\nelse:\n    raise ValueError('background start requires a non-empty bash command')","handlingStrategy":"validation","validationCode":"def block_has_command(block) -> bool:\n    cmd = block.input.get('command') if isinstance(getattr(block, 'input', None), dict) else None\n    return isinstance(cmd, str) and bool(cmd.strip())","typeGuard":"def is_runnable_bash_block(block) -> bool:\n    return (getattr(block, 'name', '') == 'bash'\n            and isinstance(block.input, dict)\n            and isinstance(block.input.get('command'), str)\n            and bool(block.input['command'].strip()))","tryCatchPattern":"try:\n    bg_id = background.start(block)\nexcept ValueError as e:\n    if 'cannot be empty' in str(e):\n        ask_model_to_regenerate_tool_call(block)  # or surface to the user\n    else:\n        raise","preventionTips":["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)."],"tags":["background-tasks","validation","user-input"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}