{"record":{"id":"1625543286f185dc","repo":"usestrix/strix","slug":"unknown-command-command","errorCode":null,"errorMessage":"Unknown command: {command}","messagePattern":"Unknown command: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"strix/interface/tui/backend/controller.py","lineNumber":277,"sourceCode":"        next_cursor, events = self.live_view.event_changes_since(cursor)\n        return next_cursor, [\n            collection_item_projection(event) for event in events[-MAX_TERMINAL_EVENTS:]\n        ]\n\n    async def handle(self, command: str, payload: dict[str, Any]) -> dict[str, Any]:\n        handlers = {\n            \"setup.add_target\": self._add_target,\n            \"setup.set_instruction\": self._set_instruction,\n            \"setup.start\": self._start,\n            \"setup.confirm_mount\": self._confirm_mount,\n            \"agent.send_message\": self._send_message,\n            \"agent.stop\": self._stop_agent,\n            \"viewer.open\": self._open_viewer,\n            \"app.quit\": self._quit,\n        }\n        handler = handlers.get(command)\n        if handler is None:\n            raise ValueError(f\"Unknown command: {command}\")\n        result = await handler(payload)\n        self.notify_changed()\n        return result\n\n    async def _add_target(self, payload: dict[str, Any]) -> dict[str, Any]:\n        self._require_setup_mutable()\n        target = self._required_string(payload, \"target\")\n        if target not in self.targets:\n            self.targets.append(target)\n        return {\"target\": target, \"total\": len(self.targets)}\n\n    async def _set_instruction(self, payload: dict[str, Any]) -> dict[str, Any]:\n        self._require_setup_mutable()\n        instruction = payload.get(\"instruction\", \"\")\n        if not isinstance(instruction, str):\n            raise TypeError(\"instruction must be a string\")\n        self.instruction = instruction.strip()\n        return {\"instruction\": self.instruction}","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/interface/tui/backend/controller.py#L259-L295","documentation":"Raised by the TUI backend controller's command dispatch (strix/interface/tui/backend/controller.py:277) when a command string is not one of the eight registered handlers (setup.add_target, setup.set_instruction, setup.start, setup.confirm_mount, agent.send_message, agent.stop, viewer.open, app.quit). It signals a frontend/backend protocol mismatch: the Go/Bubble Tea frontend sent a command name this Python controller version does not know.","triggerScenarios":"A TUI frontend and backend built from different Strix versions (stale installed binary vs updated Python package); hand-written websocket/bridge clients sending arbitrary command names; renamed/removed commands after an upgrade.","commonSituations":"Upgrading the Python package while an old compiled Go TUI binary remains on PATH (or vice versa); development against the controller with experimental command names; protocol drift after pulling main.","solutions":["Reinstall/align both halves of the TUI to the same version (make dev-install or a fresh pip/uv install) so frontend and backend share one command set.","If driving the controller programmatically, restrict sends to the registered handlers listed in the dispatch table.","Check the release notes/diff for renamed commands and update the caller accordingly."],"exampleFix":"# before\nawait controller.handle(\"setup.addTarget\", {})   # Unknown command\n\n# after\nawait controller.handle(\"setup.add_target\", {})","handlingStrategy":"validation","validationCode":"KNOWN = {\"setup.add_target\", \"setup.set_instruction\", \"setup.start\",\n         \"setup.confirm_mount\", \"agent.send_message\", \"agent.stop\",\n         \"viewer.open\", \"app.quit\"}\nif command not in KNOWN:\n    raise ValueError(f'unsupported command: {command}')","typeGuard":"def is_known_command(command: str) -> bool:\n    KNOWN = {\"setup.add_target\", \"setup.set_instruction\", \"setup.start\",\n             \"setup.confirm_mount\", \"agent.send_message\", \"agent.stop\",\n             \"viewer.open\", \"app.quit\"}\n    return command in KNOWN","tryCatchPattern":"try:\n    await controller.handle(command, payload)\nexcept ValueError as exc:\n    if 'Unknown command' in str(exc):\n        logging.error('protocol mismatch: upgrade TUI frontend/backend to same version')\n        raise SystemExit(1)\n    raise","preventionTips":["Keep the Go TUI binary and Python package at the same Strix version.","Pin one version in CI for both halves.","Never send unregistered command names from custom clients."],"tags":["tui","protocol","dispatch","version-skew"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}