langgenius/dify · error
API key not found
Error message
API key not found
What it means
HTTP 404 from `DatasetApiDeleteApi.delete` (DELETE /console/api/datasets/api-keys/<api_key_id>). The handler queries `ApiToken` by `(tenant_id, type=DATASET, id=api_key_id)`; if no row is returned it calls `console_ns.abort(404, message="API key not found")`. This is a hard not-found — the token does not exist under the current tenant or has already been deleted.
Source
Thrown at api/controllers/console/datasets/datasets.py:1182
@is_admin_or_owner_required
@rbac_permission_required(RBACResourceScope.DATASET, RBACPermission.DATASET_API_KEY_MANAGE, resource_required=False)
@account_initialization_required
@with_current_tenant_id
@with_session
def delete(self, session: Session, current_tenant_id: str, api_key_id: UUID):
api_key_id_str = str(api_key_id)
key = session.scalar(
select(ApiToken)
.where(
ApiToken.tenant_id == current_tenant_id,
ApiToken.type == self.resource_type,
ApiToken.id == api_key_id_str,
)
.limit(1)
)
if key is None:
console_ns.abort(404, message="API key not found")
# Invalidate cache before deleting from database
# Type assertion: key is guaranteed to be non-None here because abort() raises
assert key is not None # nosec - for type checker only
ApiTokenCache.delete(key.token, key.type)
session.delete(key)
return "", 204
@console_ns.route("/datasets/<uuid:dataset_id>/api-keys/<string:status>")
class DatasetEnableApiApi(Resource):
@setup_required
@login_required
@account_initialization_required
@console_ns.response(200, "Success", console_ns.models[SimpleResultResponse.__name__])
@with_current_userView on GitHub (pinned to ef8544b173)
Solutions
- Verify the `api_key_id` still exists for the current tenant before issuing the DELETE (or treat 404 as success for idempotent deletes).
- Confirm the token type matches the endpoint (`DATASET` here) — app keys live on a different route.
- Refresh the key list in the UI after each delete to prevent stale double-clicks.
- Check that the request is authenticated as the same tenant that owns the token.
Example fix
// before
await api.delete(`/datasets/api-keys/${id}`) // throws on 404
// after
try {
await api.delete(`/datasets/api-keys/${id}`)
} catch (e) {
if (e.status === 404) { /* already gone — refresh list, no error toast */ }
else throw e
} Defensive patterns
Strategy: try-catch
Validate before calling
from sqlalchemy import select
from models.model import ApiToken
def exists(session, tenant_id: str, api_key_id: str) -> bool:
return session.scalar(
select(ApiToken.id)
.where(ApiToken.tenant_id == tenant_id,
ApiToken.type == ApiTokenType.DATASET,
ApiToken.id == api_key_id)
.limit(1)
) is not None Type guard
import uuid
def is_api_key_id(v) -> bool:
try:
uuid.UUID(str(v)); return True
except (ValueError, TypeError):
return False Try / catch
try:
client.delete(f"/datasets/api-keys/{key_id}")
except HTTPError as err:
if err.response.status_code == 404:
# already deleted — refresh list, swallow
return
raise Prevention
- Treat 404 on delete as success (idempotent).
- Refresh the key list after each delete to avoid stale double-deletes.
- Confirm the token type matches the endpoint before deleting.
- Verify tenant context matches the token's owner.
When it happens
Trigger: DELETE /console/api/datasets/api-keys/<uuid> where the UUID does not match an `ApiToken` row for the current tenant with `type=DATASET`. Commonly: deleting a key twice, deleting an app-type token through the dataset endpoint, or operating against the wrong tenant.
Common situations: UI not refreshing after a delete (double-delete); user switched tenant context but the cached key list is stale; passing an `app` API key id to a dataset-key delete route (mismatched `type`).
Related errors
- export response missing data field
- reconnect stream body missing
- unknown
- usage_missing_arg
- Dataset not found for pipeline
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/9140d6a3ad586aa5.
Report an issue: GitHub.