{"record":{"id":"766431a2f6f15a8a","repo":"unslothai/unsloth","slug":"api-key-cannot-be-empty","errorCode":null,"errorMessage":"API key cannot be empty","messagePattern":"API key cannot be empty","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"studio/backend/routes/providers.py","lineNumber":317,"sourceCode":"\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\n    if base_url and base_url != existing[\"base_url\"]:\n        try:\n            base_url = validate_provider_base_url(base_url)\n        except ValueError as exc:\n            raise HTTPException(status_code = 400, detail = str(exc)) from None\n\n    replacement_api_key = None\n    if payload.encrypted_api_key:\n        credential_secrets.get_or_create_credential_encryption_key()\n        replacement_api_key = resolve_provider_api_key_or_400(\n            provider_id, payload.encrypted_api_key\n        )\n        if not replacement_api_key:\n            raise HTTPException(status_code = 400, detail = \"API key cannot be empty\")\n\n    with current_credential_write(credential):\n        if metadata_requested:\n            metadata_updates = dict(\n                id = provider_id,\n                display_name = payload.display_name,\n                base_url = base_url,\n                is_enabled = payload.is_enabled,\n                models = payload.models,\n                available_models = payload.available_models,\n            )\n            if max_output_tokens_requested:\n                metadata_updates[\"max_output_tokens\"] = payload.max_output_tokens\n            providers_db.update_provider(**metadata_updates)\n        try:\n            if replacement_api_key is not None:\n                credential_secrets.save_provider_api_key(provider_id, replacement_api_key)\n            elif payload.clear_api_key:","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/providers.py#L299-L335","documentation":"A 400 from PUT /api/providers/{provider_id}: encrypted_api_key was supplied and decrypted, but the decrypted value was empty. The server treats an explicit-but-empty key as an invalid operation — use clear_api_key to remove a key, not an empty key payload.","triggerScenarios":"Submitting the key field when the user cleared it and the client encrypts the empty string anyway; client code that always includes encrypted_api_key once the field exists.","commonSituations":"Forms where 'leave blank to keep current' is implemented by sending an encrypted empty string; whitespace-trimmed input becoming empty.","solutions":["Omit encrypted_api_key entirely to keep the current key.","Send clear_api_key: true (alone) to delete the stored key.","Only send encrypted_api_key when it decrypts to a non-empty string."],"exampleFix":"// before\nbody.encrypted_api_key = await encrypt(keyInput.value); // even when \"\"\n// after\nif (keyInput.value.trim() !== \"\") {\n  body.encrypted_api_key = await encrypt(keyInput.value.trim());\n}","handlingStrategy":"validation","validationCode":"if (typeof keyInput === \"string\" && keyInput.trim() === \"\") {\n  delete body.encrypted_api_key; // blank means keep current key, not replace-with-empty\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never encrypt and send an empty key string.","Use clear_api_key alone to remove a stored key."],"tags":["api-key","validation","providers"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}