rohitg00/ai-engineering-from-scratch · error · ValueError
review_sample must contain text
Error message
review_sample must contain text
What it means
The final content check: the sampling response's content object must carry a 'text' string. An image/audio content object, a content dict without 'text', or text of the wrong type fails here after every earlier check has passed.
Source
Thrown at certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py:425
elicitation = responses["review_goal"]
if not all(
isinstance(response, dict)
for response in (workspace_scope, review_sample, elicitation)
):
raise ValueError("inputResponses entries must be objects")
elicitation_content = elicitation.get("content", {})
if not isinstance(elicitation_content, dict):
raise ValueError("review_goal.content must be an object")
roots = workspace_scope.get("roots")
sample_content = review_sample.get("content", {})
goal = elicitation_content.get("goal")
if not isinstance(roots, list) or not isinstance(sample_content, dict):
raise ValueError("inputResponses contain invalid roots or sampling results")
if elicitation.get("action") != "accept" or not isinstance(goal, str):
raise ValueError("review_goal must be accepted with a string goal")
sample = sample_content.get("text")
if not isinstance(sample, str):
raise ValueError("review_sample must contain text")
summary = {
"goal": goal,
"rootCount": len(roots),
"sample": sample,
"topic": arguments["topic"],
}
return self._complete(
content=[{"type": "text", "text": json.dumps(summary, sort_keys=True)}],
isError=False,
)
@staticmethod
def _progress(token: str | int, progress: int, total: int, message: str) -> dict[str, Any]:
return {
"jsonrpc": "2.0",
"method": "notifications/progress",
"params": {
"progressToken": token,View on GitHub (pinned to 39ea8a1c6d)
Solutions
- Return text content: {"content": {"type": "text", "text": "..."}}
- When sampling can yield images, request or force text-only output for this tool
- Fail fast on missing text rather than sending an incomplete response
Example fix
# before
review_sample = {"content": {"type": "image", "data": "..."}}
# after
review_sample = {"content": {"type": "text", "text": "review focus: auth errors"}} Defensive patterns
Strategy: validation
Validate before calling
text = responses["review_sample"].get("content", {}).get("text")
if not isinstance(text, str):
raise ValueError("sampling response must contain text content") Type guard
def has_text_content(sample: object) -> bool:
return (
isinstance(sample, dict)
and isinstance(sample.get("content"), dict)
and isinstance(sample["content"].get("text"), str)
) Prevention
- Force text-only sampling output for tools that require text
- Validate sampling results before forwarding them
- Reject or convert multimodal content instead of forwarding it raw
When it happens
Trigger: review_sample content like {"type": "image", ...} or {"content": {}} — any createMessage result whose content lacks a string 'text' field.
Common situations: A model or sampling stub returns multimodal content; a client normalizes text away; an empty sampling result forwarded unchanged.
Related errors
- inputResponses contain invalid roots or sampling results
- -32600
- -32603
- _meta.{PROTOCOL_VERSION_KEY} is required
- _meta.{CLIENT_CAPABILITIES_KEY} is required
AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26).
Data as JSON: /api/errors/fb3fc5e2a73b6252.
Report an issue: GitHub.