usestrix/strix · error · TypeError
instruction must be a string
Error message
instruction must be a string
What it means
Raised by the TUI controller's _set_instruction handler (strix/interface/tui/backend/controller.py:293) when the payload's 'instruction' field is present but not a str (e.g. a number, dict, or null-ish sentinel passed through JSON). Python's isinstance check is the type boundary between the JSON-speaking frontend and the controller's state, so a non-string is rejected as TypeError before it can pollute the scan instruction.
Source
Thrown at strix/interface/tui/backend/controller.py:293
handler = handlers.get(command)
if handler is None:
raise ValueError(f"Unknown command: {command}")
result = await handler(payload)
self.notify_changed()
return result
async def _add_target(self, payload: dict[str, Any]) -> dict[str, Any]:
self._require_setup_mutable()
target = self._required_string(payload, "target")
if target not in self.targets:
self.targets.append(target)
return {"target": target, "total": len(self.targets)}
async def _set_instruction(self, payload: dict[str, Any]) -> dict[str, Any]:
self._require_setup_mutable()
instruction = payload.get("instruction", "")
if not isinstance(instruction, str):
raise TypeError("instruction must be a string")
self.instruction = instruction.strip()
return {"instruction": self.instruction}
async def _start(self, payload: dict[str, Any]) -> dict[str, Any]:
if self.scan_started or self._start_in_progress:
raise RuntimeError("Scan is already starting or running")
# A bare prompt launches optimistically, like a coding agent: it skips
# the network model preflight and surfaces any model error live. A named
# target keeps the preflight so a real scan does not commit blind.
verify = payload.get("verify", True)
if not isinstance(verify, bool):
raise TypeError("verify must be a boolean")
# Launching with no target mounts the working directory, so it requires
# the user's explicit confirmation rather than happening silently.
mount_working_dir = payload.get("mount_working_dir", False)
if not isinstance(mount_working_dir, bool):
raise TypeError("mount_working_dir must be a boolean")
model = (load_settings().llm.model or "").strip()View on GitHub (pinned to 8551339130)
Solutions
- Ensure the frontend/caller always sends instruction as a JSON string, coercing with String(value) / str(value) before dispatch.
- Default to omitting the key (the handler falls back to "") rather than sending null.
- Add a payload unit test asserting type str for every set_instruction dispatch.
Example fix
// before
send("setup.set_instruction", { instruction: null });
// after
send("setup.set_instruction", { instruction: String(textbox.value ?? "") }); Defensive patterns
Strategy: type-guard
Validate before calling
instruction = payload.get("instruction", "")
if not isinstance(instruction, str):
payload["instruction"] = str(instruction) Type guard
def is_string_instruction(payload: dict) -> bool:
instruction = payload.get("instruction", "")
return instruction is None or isinstance(instruction, str) Try / catch
try:
await controller.handle("setup.set_instruction", payload)
except TypeError as exc:
if 'instruction must be a string' in str(exc):
payload["instruction"] = str(payload.get("instruction") or "")
await controller.handle("setup.set_instruction", payload)
else:
raise Prevention
- Coerce textbox values to strings at the frontend boundary.
- Omit the key rather than sending null.
- Add payload contract tests for every command.
When it happens
Trigger: Sending {"command": "setup.set_instruction", "payload": {"instruction": 123}} or any non-string JSON value; a frontend bug serializing the textbox content as an object/null; scripted clients reusing a parsed JSON value of the wrong type.
Common situations: Frontend regression after input-handling changes; automated drivers building payloads from untyped data (e.g. passing parsed YAML values straight through).
Related errors
- verify must be a boolean
- mount_working_dir must be a boolean
- approved must be a boolean
- {name} must be a non-empty string
- The interactive interface could not start: {exc}
AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15).
Data as JSON: /api/errors/8ca0ac8a07a8a2c1.
Report an issue: GitHub.