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

  1. Ensure the frontend/caller always sends instruction as a JSON string, coercing with String(value) / str(value) before dispatch.
  2. Default to omitting the key (the handler falls back to "") rather than sending null.
  3. 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

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


AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15). Data as JSON: /api/errors/8ca0ac8a07a8a2c1. Report an issue: GitHub.