makeplane/plane · error · AuthenticationFailed

Given API token is not valid

Error message

Given API token is not valid

What it means

validate_api_token queries APIToken with conditions: token matches, is_active=True, user__is_active=True, and (expired_at > now OR expired_at is null). If APIToken.DoesNotExist is raised, the DRF AuthenticationFailed is thrown with this message, producing an HTTP 401. This is the standard API-token auth path in apps/api/plane/api/middleware.

Solutions

  1. Regenerate the API token in the workspace settings and update the client.
  2. Confirm the token's user account is active and not suspended.
  3. Check the token's expired_at; if set and passed, issue a new token.
  4. Verify the header name matches auth_header_name (default X-API-Key) and the value has no leading/trailing whitespace.

Example fix

# before
curl -H 'X-API-Key: stale-or-revoked' https://api/v1/...

# after
curl -H 'X-API-Key: <newly generated token>' https://api/v1/...
Defensive patterns

Strategy: try-catch

Validate before calling

# before each call, ensure token is non-empty and recently issued
import os
assert os.environ['PLANE_API_KEY'].strip(), 'missing API token'

Try / catch

try:
    client.get('/work-items/')
except AuthenticationFailed as e:
    if str(e) == 'Given API token is not valid':
        rotate_token(); retry()

Prevention

When it happens

Trigger: Request with X-API-Key header set to a token that is wrong, revoked (is_active=False), owned by a deactivated user, or past expired_at with no null expiry.

Common situations: Rotated/revoked token still used by an integration; clock skew between client and server expiring the token early; user deactivated but external script still runs; typo in the header value.

Related errors


AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12). Data as JSON: /api/errors/9262910955a359e2. Report an issue: GitHub.

Appendix: source

Thrown at apps/api/plane/api/middleware/api_authentication.py:38

    """

    www_authenticate_realm = "api"
    media_type = "application/json"
    auth_header_name = "X-Api-Key"

    def get_api_token(self, request):
        return request.headers.get(self.auth_header_name)

    def validate_api_token(self, token):
        try:
            api_token = APIToken.objects.get(
                Q(Q(expired_at__gt=timezone.now()) | Q(expired_at__isnull=True)),
                token=token,
                is_active=True,
                user__is_active=True,
            )
        except APIToken.DoesNotExist:
            raise AuthenticationFailed("Given API token is not valid")

        # save api token last used
        api_token.last_used = timezone.now()
        api_token.save(update_fields=["last_used"])
        return (api_token.user, api_token.token)

    def authenticate(self, request):
        token = self.get_api_token(request=request)
        if not token:
            return None

        # Validate the API token
        user, token = self.validate_api_token(token)
        return user, token

View on GitHub (pinned to 1c8a60f858)