{"record":{"id":"2887e87aead83444","repo":"rohitg00/ai-engineering-from-scratch","slug":"review-goal-must-be-accepted-with-a-string-goal","errorCode":null,"errorMessage":"review_goal must be accepted with a string goal","messagePattern":"review_goal must be accepted with a string goal","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py","lineNumber":422,"sourceCode":"\n        workspace_scope = responses[\"workspace_scope\"]\n        review_sample = responses[\"review_sample\"]\n        elicitation = responses[\"review_goal\"]\n        if not all(\n            isinstance(response, dict)\n            for response in (workspace_scope, review_sample, elicitation)\n        ):\n            raise ValueError(\"inputResponses entries must be objects\")\n        elicitation_content = elicitation.get(\"content\", {})\n        if not isinstance(elicitation_content, dict):\n            raise ValueError(\"review_goal.content must be an object\")\n        roots = workspace_scope.get(\"roots\")\n        sample_content = review_sample.get(\"content\", {})\n        goal = elicitation_content.get(\"goal\")\n        if not isinstance(roots, list) or not isinstance(sample_content, dict):\n            raise ValueError(\"inputResponses contain invalid roots or sampling results\")\n        if elicitation.get(\"action\") != \"accept\" or not isinstance(goal, str):\n            raise ValueError(\"review_goal must be accepted with a string goal\")\n        sample = sample_content.get(\"text\")\n        if not isinstance(sample, str):\n            raise ValueError(\"review_sample must contain text\")\n        summary = {\n            \"goal\": goal,\n            \"rootCount\": len(roots),\n            \"sample\": sample,\n            \"topic\": arguments[\"topic\"],\n        }\n        return self._complete(\n            content=[{\"type\": \"text\", \"text\": json.dumps(summary, sort_keys=True)}],\n            isError=False,\n        )\n\n    @staticmethod\n    def _progress(token: str | int, progress: int, total: int, message: str) -> dict[str, Any]:\n        return {\n            \"jsonrpc\": \"2.0\",","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py#L404-L440","documentation":"The elicitation step only satisfies the server when the user accepted the form (action == 'accept') AND content.goal is a string. Declines, cancels, or a missing/non-string goal raise this — the server treats an unaccepted goal as an invalid response rather than a retryable input.","triggerScenarios":"review_goal of {\"action\": \"decline\"}, {\"action\": \"cancel\"}, or {\"action\": \"accept\", \"content\": {}} where goal is absent or not a string.","commonSituations":"User cancels the elicitation dialog; the UI sends the goal under a different key ('value', 'text'); a form validation gap lets an empty or null goal through.","solutions":["Only resume the call after the user accepts the form with a non-empty goal string","Handle decline/cancel as a user-abort path before resuming tools/call, not as a response payload","Verify action == 'accept' and isinstance(goal, str) client-side before sending inputResponses"],"exampleFix":"# before\nreview_goal = {\"action\": \"decline\", \"content\": {}}\n\n# after\nreview_goal = {\"action\": \"accept\", \"content\": {\"goal\": \"tighten auth review\"}}","handlingStrategy":"validation","validationCode":"goal_response = responses[\"review_goal\"]\nif goal_response.get(\"action\") != \"accept\":\n    raise UserAborted(\"elicitation not accepted\")\nif not isinstance(goal_response.get(\"content\", {}).get(\"goal\"), str):\n    raise ValueError(\"accepted elicitation must carry a string goal\")","typeGuard":"def is_accepted_goal(response: object) -> bool:\n    return (\n        isinstance(response, dict)\n        and response.get(\"action\") == \"accept\"\n        and isinstance(response.get(\"content\", {}).get(\"goal\"), str)\n    )","tryCatchPattern":"try:\n    result = server.exchange(\"tools/call\", params, metadata=meta)\nexcept ValueError as exc:\n    if \"review_goal\" in str(exc):\n        rerun_elicitation()\n    raise","preventionTips":["Handle decline/cancel as a user abort before resuming","Map form fields to requestedSchema keys exactly","Block empty goals in the form UI"],"tags":["mcp","elicitation","user-input","validation"],"backgroundTag":"elicitation-not-accepted","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}