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

resume from AWAITING_TOOL requires a payload

Error message

resume from AWAITING_TOOL requires a payload

What it means

Payload precondition on Harness.resume: the session is in AWAITING_TOOL, meaning a step paused to obtain a tool result, but resume() was called with payload=None. There is no way to continue the step without the tool outcome (result or error), so the resume is rejected rather than fabricating one.

Solutions

  1. Pass the tool result payload, e.g. resume({"result": ...}) or resume({"error": ...})
  2. Have the executor always attach the captured tool outcome before resuming
  3. Distinguish resumable pauses in the UI/CLI so callers know a payload is required
  4. Test both the result and error payload paths
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at phases/19-capstone-projects/20-agent-harness-loop-contract/code/main.py:239 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/f43877710f51c929. Report an issue: GitHub.

Appendix: source

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

    def resume(self, payload: dict | None = None) -> PullRequest | SessionResult:
        if self.state == State.IDLE and self._reason.startswith("budget_exceeded"):
            self.budget.turns = 0
            self.budget.tool_calls = 0
            self.budget.started_at = time.time()
            self._reason = ""
            prev = self._prev_state
            self._prev_state = None
            if not self._plan:
                return self._begin_plan()
            if prev == State.EXECUTING:
                self.state = State.EXECUTING
            else:
                self.state = State.REFLECTING
            return self._step()
        if self.state == State.AWAITING_TOOL:
            if payload is None:
                raise ValueError("resume from AWAITING_TOOL requires a payload")
            current = self._plan[self._cursor]
            if "error" in payload:
                current.error = str(payload["error"])
                self._emit("tool.error", {"step": current.id, "error": current.error})
                self.hooks.fire("on_error", {"step": current, "error": current.error})
            else:
                current.result = payload.get("result")
                self._emit("tool.result", {"step": current.id, "result": current.result})
            self.hooks.fire("after_tool_call", {"step": current})
            self._transition(State.REFLECTING)
            return self._step()
        raise RuntimeError(f"resume() unsupported from state {self.state.value}")

    def _begin_plan(self) -> PullRequest | SessionResult:
        self._transition(State.PLANNING)
        self.hooks.fire("before_plan", {"goal": self._goal, "history": list(self._history)})
        draft = self._planner(self._goal, list(self._history))
        self._emit("plan.draft", {"steps": [s.description for s in draft]})

View on GitHub (pinned to 39ea8a1c6d)