odysseus-dev/odysseus · warning · HTTPException

endpoint_a/endpoint_b or endpoint_a_id/endpoint_b_id is requ

Error message

endpoint_a/endpoint_b or endpoint_a_id/endpoint_b_id is required

What it means

Raised by the compare route when neither an endpoint id nor a raw endpoint URL was supplied for one of the two sides. Each side must resolve to an endpoint somehow; an entirely blank side is a usage error (422), not a lookup failure.

Source

Thrown at routes/compare/compare_routes.py:144

                # Prefer an explicit endpoint id: it pins the EXACT registered
                # endpoint (and its api_key), even when two endpoints visible to
                # the caller share a base_url with different keys — a URL-only
                # match would copy whichever row sorts first, i.e. possibly the
                # wrong key. Fall back to URL resolution only for legacy / admin
                # raw-URL callers that don't send an id.
                eid = endpoint_id.strip() if isinstance(endpoint_id, str) else ""
                if eid:
                    ep = _owned_endpoint_by_id(db, eid, user)
                    if ep is None:
                        # An id the caller can't see (wrong owner / deleted) must
                        # NOT silently fall back to a same-URL row with a different
                        # key — that's exactly the mix-up ids exist to prevent.
                        raise HTTPException(404, "Model endpoint not found")
                    # The id already resolved the endpoint; ignore any raw URL the
                    # caller also sent and dial the stored config instead.
                    endpoint = ep.base_url
                elif not endpoint:
                    raise HTTPException(
                        422, "endpoint_a/endpoint_b or endpoint_a_id/endpoint_b_id is required"
                    )
                else:
                    # Resolve the supplied URL to a ModelEndpoint the caller owns
                    # (their own rows + legacy null-owner shared rows), scoped so a
                    # comparison can't borrow another user's private endpoint key.
                    base = normalize_base(endpoint)
                    ep = _owned_endpoint_by_url(db, base, user)
                # Reject *unregistered* raw URLs for signed-in non-admins; a
                # matched registered endpoint supplies an id so the caller can
                # still compare endpoints they own. Blanket-rejecting here (the
                # earlier `endpoint_id=None` call) locked non-admins out of
                # compare entirely, since compare resolves endpoints by URL with
                # no endpoint_id. Mirrors the gallery inpaint/harmonize checks.
                # Raised here (phase 1), before any session exists.
                _reject_raw_endpoint_url_for_non_admin(
                    request, user, str(ep.id) if ep is not None else None, endpoint
                )

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Send endpoint_a/endpoint_b (raw URLs you own or that are registered) or endpoint_a_id/endpoint_b_id for both sides.
  2. Prefer the *_id form; it is unambiguous about which key is used.
  3. Validate the payload client-side before POSTing.

Example fix

// before
{model_a, model_b}  // no endpoints

// after
{model_a, model_b, endpoint_a_id: "17", endpoint_b_id: "23"}
Defensive patterns

Strategy: validation

Validate before calling

assert (endpoint_a or endpoint_a_id) and (endpoint_b or endpoint_b_id), \
    'each compare side needs a URL or an id'

Type guard

type CompareSide = { endpoint?: string; endpoint_id?: string }
const sideOk = (s: CompareSide) => !!(s.endpoint?.trim() || s.endpoint_id?.trim())

Prevention

When it happens

Trigger: POST compare with endpoint_a/endpoint_b missing or empty and endpoint_a_id/endpoint_b_id also missing for at least one side; client sends model names only and assumes the server picks endpoints.

Common situations: Client schema drift — older callers sent only models; form encoding dropping empty-string fields; new frontend forgetting the endpoint pickers.

Related errors


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