{"record":{"id":"9140d6a3ad586aa5","repo":"langgenius/dify","slug":"api-key-not-found","errorCode":null,"errorMessage":"API key not found","messagePattern":"API key not found","errorType":"http","errorClass":null,"httpStatus":404,"severity":"error","filePath":"api/controllers/console/datasets/datasets.py","lineNumber":1182,"sourceCode":"    @is_admin_or_owner_required\n    @rbac_permission_required(RBACResourceScope.DATASET, RBACPermission.DATASET_API_KEY_MANAGE, resource_required=False)\n    @account_initialization_required\n    @with_current_tenant_id\n    @with_session\n    def delete(self, session: Session, current_tenant_id: str, api_key_id: UUID):\n        api_key_id_str = str(api_key_id)\n        key = session.scalar(\n            select(ApiToken)\n            .where(\n                ApiToken.tenant_id == current_tenant_id,\n                ApiToken.type == self.resource_type,\n                ApiToken.id == api_key_id_str,\n            )\n            .limit(1)\n        )\n\n        if key is None:\n            console_ns.abort(404, message=\"API key not found\")\n\n        # Invalidate cache before deleting from database\n        # Type assertion: key is guaranteed to be non-None here because abort() raises\n        assert key is not None  # nosec - for type checker only\n        ApiTokenCache.delete(key.token, key.type)\n\n        session.delete(key)\n\n        return \"\", 204\n\n\n@console_ns.route(\"/datasets/<uuid:dataset_id>/api-keys/<string:status>\")\nclass DatasetEnableApiApi(Resource):\n    @setup_required\n    @login_required\n    @account_initialization_required\n    @console_ns.response(200, \"Success\", console_ns.models[SimpleResultResponse.__name__])\n    @with_current_user","sourceCodeStart":1164,"sourceCodeEnd":1200,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/datasets/datasets.py#L1164-L1200","documentation":"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.","triggerScenarios":"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.","commonSituations":"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`).","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."],"exampleFix":"// before\nawait api.delete(`/datasets/api-keys/${id}`)  // throws on 404\n// after\ntry {\n  await api.delete(`/datasets/api-keys/${id}`)\n} catch (e) {\n  if (e.status === 404) { /* already gone — refresh list, no error toast */ }\n  else throw e\n}","handlingStrategy":"try-catch","validationCode":"from sqlalchemy import select\nfrom models.model import ApiToken\n\ndef exists(session, tenant_id: str, api_key_id: str) -> bool:\n    return session.scalar(\n        select(ApiToken.id)\n        .where(ApiToken.tenant_id == tenant_id,\n               ApiToken.type == ApiTokenType.DATASET,\n               ApiToken.id == api_key_id)\n        .limit(1)\n    ) is not None","typeGuard":"import uuid\n\ndef is_api_key_id(v) -> bool:\n    try:\n        uuid.UUID(str(v)); return True\n    except (ValueError, TypeError):\n        return False","tryCatchPattern":"try:\n    client.delete(f\"/datasets/api-keys/{key_id}\")\nexcept HTTPError as err:\n    if err.response.status_code == 404:\n        # already deleted — refresh list, swallow\n        return\n    raise","preventionTips":["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."],"tags":["not-found","api-key","dataset","http-404","idempotency"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}