langchain-ai/deepagents · error · ValueError
RubricMiddleware: `grader_state_schema` is required with `bu
Error message
RubricMiddleware: `grader_state_schema` is required with `build_grader_state`.
What it means
RubricMiddleware requires `grader_state_schema` whenever a custom `build_grader_state` callback is provided, because the custom builder is expected to produce state conforming to a schema the grader agent can consume. The pairing is mandatory so the grader's typed state stays consistent; building custom state without declaring its schema is rejected at construction.
Source
Thrown at libs/deepagents/deepagents/middleware/rubric.py:581
grader_context_schema: type[Any] | None = None,
grader_state_schema: type[AgentState[Any]] | None = None,
prepare_messages_for_grader: Callable[[list[AnyMessage]], list[AnyMessage]] | None = None,
build_grader_state: Callable[[RubricState, int], Mapping[str, Any]] | None = None,
max_iterations: int = 3,
on_evaluation: Callable[[RubricEvaluation], None] | None = None,
) -> None:
if not model:
msg = "RubricMiddleware: `model` is required."
raise ValueError(msg)
if not isinstance(max_iterations, int) or isinstance(max_iterations, bool):
msg = f"RubricMiddleware: `max_iterations` must be an int, got {type(max_iterations).__name__}."
raise TypeError(msg)
if max_iterations < 1:
msg = f"RubricMiddleware: `max_iterations` must be positive, got {max_iterations}."
raise ValueError(msg)
if grader_state_schema is None and build_grader_state is not None:
msg = "RubricMiddleware: `grader_state_schema` is required with `build_grader_state`."
raise ValueError(msg)
for name, callback in (
("prepare_messages_for_grader", prepare_messages_for_grader),
("build_grader_state", build_grader_state),
):
if callback is not None and not callable(callback):
msg = f"RubricMiddleware: `{name}` must be callable."
raise TypeError(msg)
self.max_iterations = max_iterations
self._model = model
self._model_label = _configured_model_label(model)
self._system_prompt = system_prompt or GRADER_SYSTEM_PROMPT
self._tools: list[BaseTool] = list(tools) if tools else []
self._grader_middleware = grader_middleware or ()
self._grader_context_schema = grader_context_schema
self._grader_state_schema = grader_state_schema
self._prepare_messages_for_grader = prepare_messages_for_grader
self._build_grader_state = build_grader_stateView on GitHub (pinned to a1af029e6e)
Solutions
- Pass grader_state_schema=<TypedDict or pydantic model matching what build_grader_state returns>.
- If you don't need custom state, remove the build_grader_state argument.
- Reuse an existing schema (e.g. RubricState extended) that covers your builder's keys.
- Check the docs for the expected schema shape for grader input.
Example fix
// before
RubricMiddleware(model=model, build_grader_state=build_state)
// after
class MyGraderState(RubricState):
extra_context: str
RubricMiddleware(model=model, build_grader_state=build_state, grader_state_schema=MyGraderState) Defensive patterns
Strategy: validation
Validate before calling
if build_grader_state is not None and grader_state_schema is None:
raise ValueError("build_grader_state requires grader_state_schema") Type guard
def has_builder_schema(builder: Callable | None, schema: type | None) -> bool:
return not (builder is not None and schema is None) Try / catch
try:
mw = RubricMiddleware(model=model, build_grader_state=b, grader_state_schema=s)
except ValueError as e:
if "grader_state_schema" in str(e):
mw = RubricMiddleware(model=model, grader_state_schema=RubricState) # drop the builder
else:
raise Prevention
- Provide build_grader_state and grader_state_schema together as a pair in your factory function.
- Define the schema TypedDict next to the builder in the same module.
- Add a unit test constructing the middleware with your builder.
- Check docstrings for required argument pairings.
When it happens
Trigger: RubricMiddleware(model=..., build_grader_state=my_builder) with grader_state_schema left as None.
Common situations: Adding a custom grader state builder (extra context fields for the grader) and forgetting the matching TypedDict/pydantic schema; upgrading the library where the pairing became enforced.
Related errors
- RubricMiddleware: `max_iterations` must be positive, got {ma
- max_retries must be >= 0
- Namespace tuple must not be empty.
- Namespace component at index {i} must not be empty.
- RubricMiddleware: `max_iterations` must be an int, got {type
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/01f1ba1f8812a043.
Report an issue: GitHub.