odysseus-dev/odysseus · warning · HTTPException

Invalid point format

Error message

Invalid point format

What it means

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'.

Source

Thrown at routes/gallery/gallery_routes.py:1875

        if not points and not box:
            raise HTTPException(400, "Provide at least one point, box, or object text")

        backend = _load_sam_backend()
        torch = backend["torch"]
        processor = backend["processor"]
        model = backend["model"]
        device = backend["device"]

        kwargs: Dict[str, Any] = {"return_tensors": "pt"}
        input_points = []
        if points:
            input_labels = []
            for p in points:
                try:
                    input_points.append([float(p["x"]), float(p["y"])])
                    input_labels.append(int(p.get("label", 1)))
                except Exception as exc:
                    raise HTTPException(400, "Invalid point format") from exc
            kwargs["input_points"] = [input_points]
            kwargs["input_labels"] = [input_labels]
        if box:
            if not isinstance(box, list) or len(box) != 4:
                raise HTTPException(400, "Box must be [x1, y1, x2, y2]")
            try:
                kwargs["input_boxes"] = [[[float(v) for v in box]]]
            except Exception as exc:
                raise HTTPException(400, "Invalid box format") from exc

        try:
            inputs = processor(image, **kwargs)
            model_inputs = _model_inputs_to_device(inputs, device, torch)
            with torch.no_grad():
                outputs = model(**model_inputs)
            masks = processor.image_processor.post_process_masks(
                outputs.pred_masks.detach().cpu(),
                inputs["original_sizes"].detach().cpu(),

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Send each point as {"x": <number>, "y": <number>, "label": 0|1}
  2. Strip units like 'px' and Number()-cast coordinates in the client before sending
  3. Validate the points array client-side before issuing the request

Example fix

// before
points: [[120, 80], [200, 150]]

// after
points: [{ x: 120, y: 80, label: 1 }, { x: 200, y: 150, label: 1 }]
Defensive patterns

Strategy: type-guard

Validate before calling

def normalize_points(points):
    out = []
    for p in points or []:
        out.append({
            "x": float(p["x"]),
            "y": float(p["y"]),
            "label": int(p.get("label", 1)),
        })
    return out  # raises before the request if malformed

Type guard

function isValidPoint(p: unknown): p is { x: number; y: number; label?: number } {
  if (typeof p !== 'object' || p === null) return false;
  const { x, y, label } = p as Record<string, unknown>;
  return Number.isFinite(Number(x)) && Number.isFinite(Number(y))
    && (label === undefined || Number.isInteger(Number(label)));
}
const ok = points.every(isValidPoint);

Try / catch

try:
    resp = client.post(mask_url, json=payload)
except HTTPError as e:
    if e.response.status_code == 400 and 'point format' in e.response.text:
        payload['points'] = normalize_points(payload['points'])
        resp = client.post(mask_url, json=payload)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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').

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/4b12c7df826ed46c. Report an issue: GitHub.