langchain-ai/deepagents · error · TypeError

RubricMiddleware grader returned unexpected structured_respo

Error message

RubricMiddleware grader returned unexpected structured_response of type {type(graded).__name__}.

What it means

After reading structured_response, the middleware expects a GraderResponse instance and tolerates a dict (validated via pydantic) for forward-compatibility. Any other type means the grader's response_format resolved to something unexpected, so a TypeError is raised to surface the mismatch.

Source

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

            )
        )
        return self._extract_graded(result)

    @staticmethod
    def _extract_graded(result: dict[str, Any]) -> GraderResponse:
        graded = result.get("structured_response")
        if graded is None:
            msg = "RubricMiddleware grader did not return a structured_response. The grader sub-agent must use response_format=GraderResponse."
            raise RuntimeError(msg)
        if not isinstance(graded, GraderResponse):
            # `create_agent` returns whatever the grader's response_format
            # resolves to; we expect a `GraderResponse` instance but accept
            # a `dict` for forward-compat.
            if isinstance(graded, dict):
                graded = GraderResponse.model_validate(graded)
            else:
                msg = f"RubricMiddleware grader returned unexpected structured_response of type {type(graded).__name__}."
                raise TypeError(msg)
        return graded

    def _build_grader_payload(
        self,
        state: RubricState,
        iteration: int,
        correction: str | None = None,
    ) -> str:
        """Assemble the grader's first user message.

        Wraps the caller-supplied rubric and the transcript in
        nonce-bracketed delimiters and scrubs any literal closing tags
        from the content before interpolation.

        Two modes. With no frozen criterion list the grader is asked to
        enumerate the rubric itself; once a list exists it is replayed as a
        numbered checklist and the grader is asked for that exact count, which
        keeps the criterion set from shrinking across iterations of one run.

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Configure the grader with response_format=GraderResponse.
  2. Return a plain dict matching GraderResponse fields — dicts are accepted and validated.
  3. Log type(graded) to identify which schema the grader actually used.
  4. Align any custom response model with GraderResponse or convert before returning.

Example fix

// before
grader = create_agent(model, tools=[...], response_format=MyGrade)
// after
from deepagents.middleware.rubric import GraderResponse
grader = create_agent(model, tools=[...], response_format=GraderResponse)
Defensive patterns

Strategy: try-catch

Validate before calling

raw = result.get("structured_response")
if raw is not None and not isinstance(raw, (GraderResponse, dict)):
    raise TypeError(f"expected GraderResponse or dict, got {type(raw).__name__}")

Type guard

def is_valid_graded(v: object) -> TypeGuard[GraderResponse | dict]:
    return isinstance(v, (GraderResponse, dict))

Try / catch

try:
    graded = middleware._extract_graded(result)
except TypeError as e:
    if "unexpected structured_response" in str(e):
        graded = GraderResponse.model_validate(result["structured_response"].model_dump())
    else:
        raise

Prevention

When it happens

Trigger: A custom grader agent whose response_format is a different pydantic model, so structured_response is e.g. MyOtherModel; a hand-rolled grader returning an arbitrary object in structured_response.

Common situations: Pointing RubricMiddleware at an existing agent configured with a different response format; refactoring GraderResponse and leaving an old schema in place.

Related errors


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