HumanSignal/label-studio · error · TokenExistsError

token_exists

token_exists

Error message

You already have a valid token. Please revoke it before creating a new one.

What it means

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.

Source

Thrown at label_studio/jwt_auth/views.py:187

        # Annoyingly, token_type not stored directly so we have to filter it here.
        # Shouldn't be many unexpired tokens to iterate through.
        token_objects = list(filter(None, [_maybe_get_token(token) for token in all_tokens]))
        refresh_tokens = [tok for tok in token_objects if tok['token_type'] == 'refresh']

        serializer = self.get_serializer(refresh_tokens, many=True)
        data = serializer.data
        return Response(data)

    def get_serializer_class(self):
        if self.request.method == 'POST':
            return LSAPITokenCreateSerializer
        return LSAPITokenListSerializer

    def perform_create(self, serializer):
        # Check for existing valid tokens
        existing_tokens = self.get_queryset()
        if existing_tokens.exists():
            raise TokenExistsError()

        token = self.token_class.for_user(self.request.user)
        serializer.instance = token


class LSTokenBlacklistView(TokenViewBase):
    _serializer_class = 'jwt_auth.serializers.LSAPITokenBlacklistSerializer'

    @extend_schema(
        tags=['JWT'],
        summary='Blacklist a JWT refresh token',
        description='Adds a JWT refresh token to the blacklist, preventing it from being used to obtain new access tokens.',
        responses={
            status.HTTP_204_NO_CONTENT: OpenApiResponse(description='Token was successfully blacklisted'),
            status.HTTP_404_NOT_FOUND: OpenApiResponse(
                response=TokenDetailErrorSerializer,
                description='Token is already blacklisted',
            ),

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Delete/revoke the existing token (DELETE /api/sts/tokens/{id} or the blacklist/revoke endpoint) then create the new one
  2. Reuse the existing valid token instead of minting a new one
  3. Check for HTTP 409 / code 'token_exists' in clients and revoke before retrying

Example fix

// before
POST /api/sts/tokens  # fails: token_exists
// after
DELETE /api/sts/tokens/<existing-id>
POST /api/sts/tokens
Defensive patterns

Strategy: try-catch

Validate before calling

const tokens = await fetch('/api/sts/tokens').then(r => r.json());
if (Array.isArray(tokens) && tokens.length > 0) await revokeToken(tokens[0].id);

Try / catch

try:
    token = create_token(user)
except TokenExistsError:
    existing_token = get_valid_token(user)
    revoke(existing_token)
    token = create_token(user)

Prevention

When it happens

Trigger: 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.

Common situations: Rotating tokens without first revoking the old one; double-submitting the create-token form; automation scripts that recreate tokens on each run.

Related errors


AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29). Data as JSON: /api/errors/fc0b771a24660e10. Report an issue: GitHub.