{"record":{"id":"9e2bc2e3f11203de","repo":"makeplane/plane","slug":"given-api-token-is-not-valid-9e2bc2","errorCode":null,"errorMessage":"Given API token is not valid","messagePattern":"Given API token is not valid","errorType":"exception","errorClass":"AuthenticationFailed","httpStatus":401,"severity":"error","filePath":"apps/api/plane/app/middleware/api_authentication.py","lineNumber":38,"sourceCode":"    \"\"\"\n\n    www_authenticate_realm = \"api\"\n    media_type = \"application/json\"\n    auth_header_name = \"X-Api-Key\"\n\n    def get_api_token(self, request):\n        return request.headers.get(self.auth_header_name)\n\n    def validate_api_token(self, token):\n        try:\n            api_token = APIToken.objects.get(\n                Q(Q(expired_at__gt=timezone.now()) | Q(expired_at__isnull=True)),\n                token=token,\n                is_active=True,\n                user__is_active=True,\n            )\n        except APIToken.DoesNotExist:\n            raise AuthenticationFailed(\"Given API token is not valid\")\n\n        # save api token last used\n        api_token.last_used = timezone.now()\n        api_token.save(update_fields=[\"last_used\"])\n        return (api_token.user, api_token.token)\n\n    def authenticate(self, request):\n        token = self.get_api_token(request=request)\n        if not token:\n            return None\n\n        # Validate the API token\n        user, token = self.validate_api_token(token)\n        return user, token\n","sourceCodeStart":20,"sourceCodeEnd":53,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/app/middleware/api_authentication.py#L20-L53","documentation":"Identical logic to the api/ counterpart but in apps/api/plane/app/middleware/api_authentication.py (the 'app' app's auth middleware). validate_api_token looks up an active, non-expired APIToken owned by an active user; APIToken.DoesNotExist raises DRF AuthenticationFailed -> HTTP 401. Same contract, different app namespace.","triggerScenarios":"Hitting an endpoint served by the 'app' app with a token that is invalid, inactive, expired, or whose user is inactive.","commonSituations":"Token rotated in one place but used against an 'app' endpoint elsewhere; cross-workspace token misuse; revoked token still cached by a client.","solutions":["Generate a fresh token and confirm the endpoint belongs to the expected app.","Ensure the token's user is active and not expired.","Strip whitespace/newlines from the header value.","Check logs for which app's middleware rejected it if the message alone is ambiguous."],"exampleFix":"# before\nclient = Plane(api_key='\\n<revoked-token>\\n')\n\n# after\nclient = Plane(api_key=os.environ['PLANE_API_KEY'].strip())","handlingStrategy":"try-catch","validationCode":"token = os.environ['PLANE_API_KEY'].strip()\nassert token and not token.startswith(' '), 'clean token required'","typeGuard":null,"tryCatchPattern":"try:\n    call_app_endpoint(token)\nexcept AuthenticationFailed as e:\n    if 'not valid' in str(e):\n        token = regenerate_token(); call_app_endpoint(token)","preventionTips":["Keep api/ vs app/ tokens consistent after rotation","Strip whitespace/newlines","Confirm token's user is active and non-expired","Log which middleware rejected if ambiguous"],"tags":["auth","api-token","django-rest","authentication"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}