langchain-ai/deepagents · error · ValueError

RubricMiddleware: `build_grader_state` cannot set `messages`

Error message

RubricMiddleware: `build_grader_state` cannot set `messages`.

What it means

`build_grader_state` customizes the grader's input state, but `messages` is reserved: the middleware always sets messages itself to a single HumanMessage containing the built grader payload. A builder that returns a dict containing a `messages` key would overwrite that payload, so it's rejected at runtime with ValueError.

Source

Thrown at libs/deepagents/deepagents/middleware/rubric.py:1011

        keeps untrusted transcript content from being read as instructions.

        Args:
            state: Agent state, read for the rubric and transcript.
            iteration: Zero-based grading iteration.
            correction: Feedback about a previous unusable response, if any.

        Returns:
            The nested grader's input state.
        """
        grader_state = state
        if self._prepare_messages_for_grader:
            grader_state = RubricState(**state)
            grader_state["messages"] = self._prepare_messages_for_grader(list(state.get("messages", [])))
        payload = self._build_grader_payload(grader_state, iteration, correction)
        grader_input = dict(self._build_grader_state(grader_state, iteration)) if self._build_grader_state else {}
        if "messages" in grader_input:
            msg = "RubricMiddleware: `build_grader_state` cannot set `messages`."
            raise ValueError(msg)
        grader_input["messages"] = [HumanMessage(content=payload)]
        return grader_input

    def _invoke_grader(
        self,
        state: RubricState,
        iteration: int,
        correction: str | None = None,
        *,
        context: object | None = None,
    ) -> GraderResponse:
        """Run one grader call while preserving nested graph inputs.

        This is the per-call extension point beneath `_grade`'s coverage retry.
        Overrides should forward `correction` and `context` when delegating here.
        The context is LangGraph's static runtime context, passed through so a
        nested grader using a context schema receives the same run dependencies.
        Grader input must continue through `_grader_input`, which delegates to

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Return a dict without the `messages` key: {k: v for k, v in state.items() if k != 'messages'}.
  2. Use prepare_messages_for_grader if you want to shape the conversation content shown to the grader.
  3. Pass custom instructions via system_prompt instead of injecting messages.
  4. Extend grader_state_schema for extra context fields rather than messages.

Example fix

// before
def build_grader_state(state, iteration):
    return dict(state)  # includes messages
// after
def build_grader_state(state, iteration):
    return {k: v for k, v in state.items() if k != "messages"} | {"iteration": iteration}
Defensive patterns

Strategy: try-catch

Validate before calling

def my_builder(state, iteration):
    out = {k: v for k, v in state.items() if k != "messages"}
    assert "messages" not in out
    return out

Type guard

def is_grader_input(d: dict) -> bool:
    return "messages" not in d

Try / catch

try:
    result = middleware._invoke_grader(state)
except ValueError as e:
    if "cannot set `messages`" in str(e):
        fix_builder_to_exclude_messages()
    else:
        raise

Prevention

When it happens

Trigger: A build_grader_state callback that does dict(state) or returns a state including state["messages"] instead of only extra keys.

Common situations: Copy-pasting the full state into the builder output; trying to inject custom messages/examples for the grader (the supported route is prepare_messages_for_grader or the payload via system_prompt).

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/5d81b96e4bbd5436. Report an issue: GitHub.