{"record":{"id":"700ccbb8d177e7e6","repo":"unslothai/unsloth","slug":"cannot-replace-and-clear-an-api-key-in-the-same-re","errorCode":null,"errorMessage":"Cannot replace and clear an API key in the same request","messagePattern":"Cannot replace and clear an API key in the same request","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"studio/backend/routes/providers.py","lineNumber":285,"sourceCode":"\n    existing_info = get_provider_info(existing[\"provider_type\"]) or {}\n    max_output_tokens_requested = \"max_output_tokens\" in payload.model_fields_set\n    _validate_max_output_tokens_contract(\n        existing[\"provider_type\"],\n        max_output_tokens_requested,\n        payload.max_output_tokens,\n    )\n    _validate_provider_auth_contract(\n        existing_info,\n        encrypted_api_key = payload.encrypted_api_key,\n        base_url = payload.base_url,\n        models = payload.models,\n        updating = True,\n        clear_api_key = payload.clear_api_key,\n    )\n\n    if payload.clear_api_key and payload.encrypted_api_key:\n        raise HTTPException(\n            status_code = 400,\n            detail = \"Cannot replace and clear an API key in the same request\",\n        )\n\n    metadata_fields = {\n        \"display_name\",\n        \"base_url\",\n        \"is_enabled\",\n        \"models\",\n        \"available_models\",\n        \"max_output_tokens\",\n    }\n    metadata_requested = bool(payload.model_fields_set & metadata_fields)\n\n    # Only a *changed* base URL is validated. The dialog re-sends the stored value\n    # on every edit, so validating an unchanged legacy row would lock the user out\n    # of editing its models or API key. Outbound use is still checked.\n    base_url = payload.base_url","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/providers.py#L267-L303","documentation":"A 400 from PUT /api/providers/{provider_id}: the request set both clear_api_key=true and a non-empty encrypted_api_key. These are mutually exclusive operations — replacing a key and clearing it cannot happen in one atomic request — so the backend rejects the combination before touching storage.","triggerScenarios":"An edit form that submits the key field from a 'replace key' input while a separate 'remove key' checkbox is also ticked; client code that always includes clear_api_key and happens to include a key payload.","commonSituations":"Dialogs offering both actions simultaneously; state bugs where a previously-ticked clear checkbox is never reset; bulk-sync scripts sending superset payloads.","solutions":["Send either encrypted_api_key (replace) or clear_api_key: true (remove), never both.","Reset the 'clear key' checkbox whenever the user types a new key in the form.","In API clients, make these two distinct code paths or buttons."],"exampleFix":"// before\nawait putProvider(id, { encrypted_api_key: newKey, clear_api_key: clearChecked });\n// after\nconst body = { display_name };\nif (newKey) body.encrypted_api_key = newKey;\nelse if (clearChecked) body.clear_api_key = true;","handlingStrategy":"validation","validationCode":"if (body.clear_api_key && body.encrypted_api_key) {\n  throw new Error(\"Send either encrypted_api_key or clear_api_key, not both\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model replace-key and clear-key as two distinct actions in the UI.","Untick 'clear' as soon as a new key is entered."],"tags":["api-key","validation","mutually-exclusive"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}