unslothai/unsloth · error · HTTPException

Choose only curated Codex models.

Error message

Choose only curated Codex models.

What it means

A 400 from _validate_provider_auth_contract: for a chatgpt_oauth provider, the models list was supplied but was empty or contained at least one model outside the provider's curated default_models set. ChatGPT subscriptions expose only a fixed, curated Codex model list; arbitrary model names are rejected.

Source

Thrown at studio/backend/routes/providers.py:112


def _validate_provider_auth_contract(
    info: dict,
    *,
    encrypted_api_key: str | None,
    base_url: str | None,
    models: list[str] | None,
    updating: bool,
    clear_api_key: bool = False,
) -> None:
    if info.get("auth_kind") != "chatgpt_oauth":
        return
    if encrypted_api_key or clear_api_key:
        raise HTTPException(status_code = 400, detail = "ChatGPT subscriptions do not use API keys.")
    if base_url is not None and (not updating or base_url != info["base_url"]):
        raise HTTPException(status_code = 400, detail = "ChatGPT subscription routing is fixed.")
    if models is not None and (not models or not set(models).issubset(set(info["default_models"]))):
        raise HTTPException(status_code = 400, detail = "Choose only curated Codex models.")


def _validate_max_output_tokens_contract(
    provider_type: str,
    field_was_set: bool,
    value: Optional[int] = None,
) -> None:
    """Reject a non-null override on a provider type with its own documented caps.

    An explicit null is allowed through everywhere: the dialog shows the field for rows
    it displays as Custom but the backend stores as `openai`, and a blank field
    serialises as null, so rejecting it failed every unrelated edit of those rows.
    Clearing an override that cannot exist is a no-op.
    """
    if field_was_set and value is not None and provider_type != "custom":
        raise HTTPException(
            status_code = 400,
            detail = "Max Tokens limit can only be overridden for generic Custom providers.",

View on GitHub (pinned to 203007d190)

Solutions

  1. Send models as a subset of the provider's default_models (visible via GET /api/providers/registry or the provider row).
  2. Send null/omit the field to keep the defaults instead of sending an empty array.
  3. If you need a non-curated model, use an API-key-based provider type.

Example fix

// before
await putProvider(id, { models: [] }); // -> 400
// after
await putProvider(id, { models: curatedModels.filter(m => selected.has(m)) });
// or omit `models` entirely to keep defaults
Defensive patterns

Strategy: validation

Validate before calling

const allowed = new Set(info.default_models ?? []);
const models = requestedModels.filter(m => allowed.has(m));
if (models.length === 0) delete body.models; // never send [] to a subscription provider
else body.models = models;

Prevention

When it happens

Trigger: PUT with models: [] (explicit empty list); models containing a model name not in info['default_models'] (e.g. an OpenAI API model id); syncing model lists from an OpenAI-API provider onto a subscription provider.

Common situations: Copy-pasting model names between provider types; UI 'select models' control submitting an empty selection instead of null; hard-coded model lists in client code.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/ba98c5c827c4f01d. Report an issue: GitHub.