rohitg00/ai-engineering-from-scratch · error · RuntimeError

_step requires EXECUTING/REFLECTING, got

Error message

_step requires EXECUTING/REFLECTING, got {self.state.value}

What it means

Internal invariant at the top of Harness._step: the step executor is only meaningful while the session is EXECUTING or REFLECTING (IDLE is redirected to _begin_plan earlier). Reaching _step in any other state means the state machine and the step loop have desynchronized — an internal bug, not a caller input error.

Solutions

  1. Audit _transition calls in _begin_plan, _step, and _pause for a missing or out-of-order transition
  2. Reproduce with a trace of emitted events to find where the state diverged
  3. Add state assertions at each loop entry point as a tripwire
  4. Ensure resume paths set the state via _transition, not by direct assignment to self.state
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at phases/19-capstone-projects/20-agent-harness-loop-contract/code/main.py:281 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26). Data as JSON: /api/errors/af3fed48dd9f19f0. Report an issue: GitHub.

Appendix: source

Thrown at phases/19-capstone-projects/20-agent-harness-loop-contract/code/main.py:281

            return self._complete("no_plan")
        self._transition(State.EXECUTING)
        return self._step()

    def _step(self) -> PullRequest | SessionResult:
        if self.state == State.IDLE:
            return self._begin_plan()
        budget_hit = self._check_budget()
        if budget_hit is not None:
            return budget_hit
        if self.state == State.REFLECTING:
            self._cursor += 1
            self.budget.turns += 1
            if self._cursor >= len(self._plan):
                return self._complete("goal_met")
            self._transition(State.EXECUTING)
            return self._step()
        if self.state != State.EXECUTING:
            raise RuntimeError(f"_step requires EXECUTING/REFLECTING, got {self.state.value}")
        step = self._plan[self._cursor]
        self.hooks.fire("before_step", {"step": step})
        self._emit("step.start", {"step_id": step.id, "desc": step.description})
        if step.requires_tool:
            try:
                self.hooks.fire("before_tool_call", {"step": step})
            except HookAbort as exc:
                step.error = f"hook_abort:{exc}"
                self._emit("tool.error", {"step": step.id, "error": step.error})
                self.hooks.fire("on_error", {"step": step, "error": step.error})
                self._transition(State.REFLECTING)
                return self._step()
            self.budget.tool_calls += 1
            self._emit("tool.call", {"step": step.id, "tool": step.tool_name, "args": step.tool_args})
            self._transition(State.AWAITING_TOOL)
            self._emit("step.end", {"step_id": step.id, "outcome": "awaiting_tool"})
            self.hooks.fire("after_step", {"step": step, "outcome": "awaiting_tool"})
            return PullRequest(

View on GitHub (pinned to 39ea8a1c6d)