warpdotdev/warp · error · ValueError
Expected JSON object with 'reviews' key
Error message
Expected JSON object with 'reviews' key
What it means
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.
Source
Thrown at resources/bundled/skills/create-skill/eval-viewer/generate_review.py:368
data = b"{}"
if self.feedback_path.exists():
data = self.feedback_path.read_bytes()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
else:
self.send_error(404)
def do_POST(self) -> None:
if self.path == "/api/feedback":
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length)
try:
data = json.loads(body)
if not isinstance(data, dict) or "reviews" not in data:
raise ValueError("Expected JSON object with 'reviews' key")
self.feedback_path.write_text(json.dumps(data, indent=2) + "\n")
resp = b'{"ok":true}'
self.send_response(200)
except (json.JSONDecodeError, OSError, ValueError) as e:
resp = json.dumps({"error": str(e)}).encode()
self.send_response(500)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(resp)))
self.end_headers()
self.wfile.write(resp)
else:
self.send_error(404)
def log_message(self, format: str, *args: object) -> None:
# Suppress request logging to keep terminal clean
pass
View on GitHub (pinned to e72fd7aacb)
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
Example fix
# before
curl -X POST -d '[{"file":"q1","pass":true}]' http://localhost:8765/api/feedback
# 500 {"error": "Expected JSON object with 'reviews' key"}
# after
curl -X POST -H 'Content-Type: application/json' \
-d '{"reviews": [{"file":"q1","pass":true}]}' \
http://localhost:8765/api/feedback
# {"ok":true} Defensive patterns
Strategy: validation
Validate before calling
if (!Array.isArray(reviews)) {
throw new TypeError('reviews must be an array')
}
const body = JSON.stringify({ reviews })
const res = await fetch('/api/feedback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body,
}) Type guard
function isFeedbackPayload(value) {
return typeof value === 'object' && value !== null && Array.isArray(value.reviews)
} Try / catch
const res = await fetch('/api/feedback', { method: 'POST', body })
if (!res.ok) {
const { error } = await res.json()
throw new Error(`feedback rejected: ${error}`)
} Prevention
- 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
When it happens
Trigger: 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.)
Common situations: 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.
Related errors
- MCP server '{name}' config must be a JSON object
- createSemanticTokens: mode "${modeName}" not found in modeId
- schema_unavailable_or_invalid
- invalid_json
- Invalid repo format: '{}'. Expected 'owner/repo' or 'https:/
AI-assisted analysis of warpdotdev/warp@e72fd7aacb (2026-08-16).
Data as JSON: /api/errors/fd7fda9a5c438753.
Report an issue: GitHub.