{"record":{"id":"fc0b771a24660e10","repo":"HumanSignal/label-studio","slug":"token-exists","errorCode":"token_exists","errorMessage":"You already have a valid token. Please revoke it before creating a new one.","messagePattern":"You already have a valid token\\. Please revoke it before creating a new one\\.","errorType":"http","errorClass":"TokenExistsError","httpStatus":409,"severity":"error","filePath":"label_studio/jwt_auth/views.py","lineNumber":187,"sourceCode":"        # Annoyingly, token_type not stored directly so we have to filter it here.\n        # Shouldn't be many unexpired tokens to iterate through.\n        token_objects = list(filter(None, [_maybe_get_token(token) for token in all_tokens]))\n        refresh_tokens = [tok for tok in token_objects if tok['token_type'] == 'refresh']\n\n        serializer = self.get_serializer(refresh_tokens, many=True)\n        data = serializer.data\n        return Response(data)\n\n    def get_serializer_class(self):\n        if self.request.method == 'POST':\n            return LSAPITokenCreateSerializer\n        return LSAPITokenListSerializer\n\n    def perform_create(self, serializer):\n        # Check for existing valid tokens\n        existing_tokens = self.get_queryset()\n        if existing_tokens.exists():\n            raise TokenExistsError()\n\n        token = self.token_class.for_user(self.request.user)\n        serializer.instance = token\n\n\nclass LSTokenBlacklistView(TokenViewBase):\n    _serializer_class = 'jwt_auth.serializers.LSAPITokenBlacklistSerializer'\n\n    @extend_schema(\n        tags=['JWT'],\n        summary='Blacklist a JWT refresh token',\n        description='Adds a JWT refresh token to the blacklist, preventing it from being used to obtain new access tokens.',\n        responses={\n            status.HTTP_204_NO_CONTENT: OpenApiResponse(description='Token was successfully blacklisted'),\n            status.HTTP_404_NOT_FOUND: OpenApiResponse(\n                response=TokenDetailErrorSerializer,\n                description='Token is already blacklisted',\n            ),","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/jwt_auth/views.py#L169-L205","documentation":"Raised by LSTokenCreateView.perform_create (serializing with code 'token_exists') when the user attempts to create a new API token while still having a valid one. Label Studio enforces a single active token per user; the existing token must be revoked first.","triggerScenarios":"POST to the LSAPIToken create endpoint (/api/sts/tokens or similar) when get_queryset() returns at least one existing non-revoked token for the request user.","commonSituations":"Rotating tokens without first revoking the old one; double-submitting the create-token form; automation scripts that recreate tokens on each run.","solutions":["Delete/revoke the existing token (DELETE /api/sts/tokens/{id} or the blacklist/revoke endpoint) then create the new one","Reuse the existing valid token instead of minting a new one","Check for HTTP 409 / code 'token_exists' in clients and revoke before retrying"],"exampleFix":"// before\nPOST /api/sts/tokens  # fails: token_exists\n// after\nDELETE /api/sts/tokens/<existing-id>\nPOST /api/sts/tokens","handlingStrategy":"try-catch","validationCode":"const tokens = await fetch('/api/sts/tokens').then(r => r.json());\nif (Array.isArray(tokens) && tokens.length > 0) await revokeToken(tokens[0].id);","typeGuard":null,"tryCatchPattern":"try:\n    token = create_token(user)\nexcept TokenExistsError:\n    existing_token = get_valid_token(user)\n    revoke(existing_token)\n    token = create_token(user)","preventionTips":["Always revoke before rotating tokens","Cache and reuse the existing valid token instead of minting new ones","Make token rotation idempotent in automation scripts"],"tags":["auth","token","api","conflict"],"backgroundTag":"token-already-exists","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}