{"record":{"id":"fd7fda9a5c438753","repo":"warpdotdev/warp","slug":"expected-json-object-with-reviews-key","errorCode":null,"errorMessage":"Expected JSON object with 'reviews' key","messagePattern":"Expected JSON object with 'reviews' key","errorType":"validation","errorClass":"ValueError","httpStatus":500,"severity":"error","filePath":"resources/bundled/skills/create-skill/eval-viewer/generate_review.py","lineNumber":368,"sourceCode":"            data = b\"{}\"\n            if self.feedback_path.exists():\n                data = self.feedback_path.read_bytes()\n            self.send_response(200)\n            self.send_header(\"Content-Type\", \"application/json\")\n            self.send_header(\"Content-Length\", str(len(data)))\n            self.end_headers()\n            self.wfile.write(data)\n        else:\n            self.send_error(404)\n\n    def do_POST(self) -> None:\n        if self.path == \"/api/feedback\":\n            length = int(self.headers.get(\"Content-Length\", 0))\n            body = self.rfile.read(length)\n            try:\n                data = json.loads(body)\n                if not isinstance(data, dict) or \"reviews\" not in data:\n                    raise ValueError(\"Expected JSON object with 'reviews' key\")\n                self.feedback_path.write_text(json.dumps(data, indent=2) + \"\\n\")\n                resp = b'{\"ok\":true}'\n                self.send_response(200)\n            except (json.JSONDecodeError, OSError, ValueError) as e:\n                resp = json.dumps({\"error\": str(e)}).encode()\n                self.send_response(500)\n            self.send_header(\"Content-Type\", \"application/json\")\n            self.send_header(\"Content-Length\", str(len(resp)))\n            self.end_headers()\n            self.wfile.write(resp)\n        else:\n            self.send_error(404)\n\n    def log_message(self, format: str, *args: object) -> None:\n        # Suppress request logging to keep terminal clean\n        pass\n\n","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/warpdotdev/warp/blob/e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc/resources/bundled/skills/create-skill/eval-viewer/generate_review.py#L350-L386","documentation":"The eval-viewer's local HTTP server accepts POSTs only at /api/feedback and requires the body to be a JSON object containing a 'reviews' key; anything else raises this ValueError and the server responds 500 with {\"error\": \"Expected JSON object with 'reviews' key\"}. The payload is written verbatim to the feedback file, so the top-level shape is fixed.","triggerScenarios":"Posting a JSON array of reviews, an object keyed 'review', 'results', or 'evaluations', or a bare single review object — anything but {\"reviews\": ...}. (Malformed JSON raises JSONDecodeError instead and 500s with that different message; a wrong path 404s.)","commonSituations":"curl scripts written against a different viewer version; a frontend form serializing the array directly instead of wrapping it; pasting an eval-results file (different top-level shape) as the feedback body.","solutions":["Wrap the payload: curl -X POST -d '{\"reviews\": [...]}' http://localhost:PORT/api/feedback","Send a correct Content-Length (the server reads exactly that many bytes) and Content-Type: application/json","On any 500, read the response body — the error field echoes the exact reason"],"exampleFix":"# before\ncurl -X POST -d '[{\"file\":\"q1\",\"pass\":true}]' http://localhost:8765/api/feedback\n# 500 {\"error\": \"Expected JSON object with 'reviews' key\"}\n\n# after\ncurl -X POST -H 'Content-Type: application/json' \\\n  -d '{\"reviews\": [{\"file\":\"q1\",\"pass\":true}]}' \\\n  http://localhost:8765/api/feedback\n# {\"ok\":true}","handlingStrategy":"validation","validationCode":"if (!Array.isArray(reviews)) {\n  throw new TypeError('reviews must be an array')\n}\nconst body = JSON.stringify({ reviews })\nconst res = await fetch('/api/feedback', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body,\n})","typeGuard":"function isFeedbackPayload(value) {\n  return typeof value === 'object' && value !== null && Array.isArray(value.reviews)\n}","tryCatchPattern":"const res = await fetch('/api/feedback', { method: 'POST', body })\nif (!res.ok) {\n  const { error } = await res.json()\n  throw new Error(`feedback rejected: ${error}`)\n}","preventionTips":["Always wrap the array: { reviews: [...] }","Send Content-Type: application/json and a correct body length","Read the error field on any 500 — it names the exact violation"],"tags":["http","json","validation","eval-viewer"],"backgroundTag":null,"analyzedSha":"e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc","analyzedAt":"2026-08-16T08:27:25.381Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}