{"record":{"id":"4b12c7df826ed46c","repo":"odysseus-dev/odysseus","slug":"invalid-point-format","errorCode":null,"errorMessage":"Invalid point format","messagePattern":"Invalid point format","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"routes/gallery/gallery_routes.py","lineNumber":1875,"sourceCode":"        if not points and not box:\n            raise HTTPException(400, \"Provide at least one point, box, or object text\")\n\n        backend = _load_sam_backend()\n        torch = backend[\"torch\"]\n        processor = backend[\"processor\"]\n        model = backend[\"model\"]\n        device = backend[\"device\"]\n\n        kwargs: Dict[str, Any] = {\"return_tensors\": \"pt\"}\n        input_points = []\n        if points:\n            input_labels = []\n            for p in points:\n                try:\n                    input_points.append([float(p[\"x\"]), float(p[\"y\"])])\n                    input_labels.append(int(p.get(\"label\", 1)))\n                except Exception as exc:\n                    raise HTTPException(400, \"Invalid point format\") from exc\n            kwargs[\"input_points\"] = [input_points]\n            kwargs[\"input_labels\"] = [input_labels]\n        if box:\n            if not isinstance(box, list) or len(box) != 4:\n                raise HTTPException(400, \"Box must be [x1, y1, x2, y2]\")\n            try:\n                kwargs[\"input_boxes\"] = [[[float(v) for v in box]]]\n            except Exception as exc:\n                raise HTTPException(400, \"Invalid box format\") from exc\n\n        try:\n            inputs = processor(image, **kwargs)\n            model_inputs = _model_inputs_to_device(inputs, device, torch)\n            with torch.no_grad():\n                outputs = model(**model_inputs)\n            masks = processor.image_processor.post_process_masks(\n                outputs.pred_masks.detach().cpu(),\n                inputs[\"original_sizes\"].detach().cpu(),","sourceCodeStart":1857,"sourceCodeEnd":1893,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/gallery/gallery_routes.py#L1857-L1893","documentation":"HTTP 400 raised while parsing the points array of a SAM mask request. Each point must be an object with numeric 'x'/'y' keys and an optional integer 'label'; float()/int() conversion failing (missing key, string like 'abc', None, nested list) triggers this. The original exception is chained with 'from exc'.","triggerScenarios":"POST with points=[{\"x\": \"abc\"}], points=[[120, 80]] (array instead of object), point missing the 'y' key, or label set to \"foreground\" instead of 0/1.","commonSituations":"Frontend migrating from an older [x, y] tuple format to the {x, y} object format; label sent as a string; coordinates serialized as None after a null click; client code passing pixel strings from CSS values (e.g. '120px').","solutions":["Send each point as {\"x\": <number>, \"y\": <number>, \"label\": 0|1}","Strip units like 'px' and Number()-cast coordinates in the client before sending","Validate the points array client-side before issuing the request"],"exampleFix":"// before\npoints: [[120, 80], [200, 150]]\n\n// after\npoints: [{ x: 120, y: 80, label: 1 }, { x: 200, y: 150, label: 1 }]","handlingStrategy":"type-guard","validationCode":"def normalize_points(points):\n    out = []\n    for p in points or []:\n        out.append({\n            \"x\": float(p[\"x\"]),\n            \"y\": float(p[\"y\"]),\n            \"label\": int(p.get(\"label\", 1)),\n        })\n    return out  # raises before the request if malformed","typeGuard":"function isValidPoint(p: unknown): p is { x: number; y: number; label?: number } {\n  if (typeof p !== 'object' || p === null) return false;\n  const { x, y, label } = p as Record<string, unknown>;\n  return Number.isFinite(Number(x)) && Number.isFinite(Number(y))\n    && (label === undefined || Number.isInteger(Number(label)));\n}\nconst ok = points.every(isValidPoint);","tryCatchPattern":"try:\n    resp = client.post(mask_url, json=payload)\nexcept HTTPError as e:\n    if e.response.status_code == 400 and 'point format' in e.response.text:\n        payload['points'] = normalize_points(payload['points'])\n        resp = client.post(mask_url, json=payload)\n    else:\n        raise","preventionTips":["Type the points array in the client (TS interface or pydantic model) so malformed shapes fail at compile/validation time","Convert click-event coordinates with Number() and strip CSS units before adding to the array","Write a unit test asserting a legacy [x, y] tuple payload is rejected client-side"],"tags":["http-400","validation","points","sam","type-coercion"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}