unslothai/unsloth · error · HTTPException

ChatGPT subscriptions do not use API keys.

Error message

ChatGPT subscriptions do not use API keys.

What it means

A 400 from _validate_provider_auth_contract: the provider's auth_kind is 'chatgpt_oauth' (a ChatGPT subscription authenticated via OAuth), and the request included encrypted_api_key or set clear_api_key. ChatGPT subscription providers authenticate through OAuth tokens, not API keys, so key material in the request is a contract violation.

Source

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

        max_output_tokens = row.get("max_output_tokens"),
        created_at = row["created_at"],
        updated_at = row["updated_at"],
    )


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

View on GitHub (pinned to 203007d190)

Solutions

  1. Omit encrypted_api_key and clear_api_key entirely when auth_kind is chatgpt_oauth.
  2. If you meant to use an OpenAI API key, create a provider of an API-key type instead of the ChatGPT subscription type.
  3. In shared form code, blank out key fields for subscription-based providers before submit.

Example fix

// before
await putProvider(id, {
  display_name: name,
  encrypted_api_key: keyField, // always sent
});
// after
const body = { display_name: name };
if (info.auth_kind !== "chatgpt_oauth" && keyField) {
  body.encrypted_api_key = keyField;
}
Defensive patterns

Strategy: validation

Validate before calling

const isSubscription = (info) => info?.auth_kind === "chatgpt_oauth";
if (isSubscription(info)) { delete body.encrypted_api_key; delete body.clear_api_key; }

Type guard

function isOAuthProvider(info: unknown): info is { auth_kind: "chatgpt_oauth"; default_models: string[]; base_url: string } {
  return typeof info === "object" && info !== null && (info as any).auth_kind === "chatgpt_oauth";
}

Prevention

When it happens

Trigger: POST/PUT /api/providers with provider_type whose auth_kind is chatgpt_oauth and a non-null encrypted_api_key; sending clear_api_key=true on a ChatGPT subscription row; a generic 'edit provider' form that always submits the key field.

Common situations: Frontend form reused between OpenAI-API and ChatGPT-subscription provider types; users pasting an OpenAI API key into a subscription provider; automated upsert code that includes every field regardless of auth kind.

Related errors


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