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_user

View on GitHub (pinned to ef8544b173)

Solutions

  1. Verify the `api_key_id` still exists for the current tenant before issuing the DELETE (or treat 404 as success for idempotent deletes).
  2. Confirm the token type matches the endpoint (`DATASET` here) — app keys live on a different route.
  3. Refresh the key list in the UI after each delete to prevent stale double-clicks.
  4. 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

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


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/9140d6a3ad586aa5. Report an issue: GitHub.