{"record":{"id":"b1adc1a42359c48f","repo":"apache/superset","slug":"failed-to-parse-token","errorCode":null,"errorMessage":"Failed to parse token","messagePattern":"Failed to parse token","errorType":"http","errorClass":"AsyncQueryTokenException","httpStatus":401,"severity":"error","filePath":"superset/async_events/async_query_manager.py","lineNumber":279,"sourceCode":"        ).hexdigest()\n        return f\"guest-{digest}\"\n\n    def parse_channel_id_from_request(self, req: Request) -> str:\n        # pylint: disable=import-outside-toplevel\n        from superset import security_manager\n\n        if guest_user := security_manager.get_current_guest_user_if_guest():\n            return self.get_guest_user_channel_id(guest_user)\n\n        token = req.cookies.get(self._jwt_cookie_name)\n        if not token:\n            raise AsyncQueryTokenException(\"Token not preset\")\n\n        try:\n            return jwt.decode(token, self._jwt_secret, algorithms=[\"HS256\"])[\"channel\"]\n        except Exception as ex:\n            logger.warning(\"Parse jwt failed\", exc_info=True)\n            raise AsyncQueryTokenException(\"Failed to parse token\") from ex\n\n    def init_job(self, channel_id: str, user_id: Optional[int]) -> dict[str, Any]:\n        job_id = str(uuid.uuid4())\n        self._register_cancellable_job(job_id, channel_id, user_id)\n        return build_job_metadata(\n            channel_id, job_id, user_id, status=self.STATUS_PENDING\n        )\n\n    def _job_registry_key(self, job_id: str) -> str:\n        return f\"{self._stream_prefix}{self._JOB_REGISTRY_PREFIX}{job_id}\"\n\n    def _register_cancellable_job(\n        self, job_id: str, channel_id: str, user_id: Optional[int]\n    ) -> None:\n        \"\"\"\n        Persist the identity a later cancel request must match. Keyed by\n        ``job_id`` (also the Celery task id — see ``submit_chart_data_job``) so\n        the cancel endpoint can authorize the caller against the job's original","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/async_events/async_query_manager.py#L261-L297","documentation":"AsyncQueryTokenException('Failed to parse token') wraps any exception raised while decoding the async-events JWT cookie with jwt.decode(token, secret, algorithms=['HS256']). It fires when the cookie exists but is not a valid HS256 JWT signed with the configured GLOBAL_ASYNC_QUERIES_JWT_SECRET, or when the decoded payload lacks the 'channel' claim. The underlying exception is logged as a warning with a traceback before re-raising.","triggerScenarios":"The GAQ JWT secret was changed/rotated so cookies issued under the old secret no longer verify; a stale cookie from a different Superset instance or environment (e.g. port 8088 dev vs prod sharing localhost); the cookie was truncated or mangled by a proxy; an expired token (exp claim passed); a hand-crafted cookie value.","commonSituations":"Rotating the JWT secret during a security push and forgetting that browsers hold old cookies; running multiple Superset versions side by side on the same host so cookies collide on name; deployments behind TLS terminators or CDN that rewrite cookies; clock skew making tokens appear expired.","solutions":["Have the user clear the Superset cookies (or log out/in) so a fresh token is issued with the current secret.","If the secret was rotated, ensure ALL webserver and worker pods share the new value; inconsistent secrets across replicas cause intermittent failures.","Give each environment a distinct GLOBAL_ASYNC_QUERIES_JWT_COOKIE_NAME to avoid cookie collisions between instances on the same domain.","Check the webserver log line 'Parse jwt failed' for the underlying jwt error (ExpiredSignature, InvalidSignature, DecodeError) to pick the right fix."],"exampleFix":"# deployment fix: unique cookie names per environment\n# before\nGLOBAL_ASYNC_QUERIES_JWT_COOKIE_NAME = \"async-token\"\n# (dev and prod on same domain overwrite each other's cookies)\n\n# after\nGLOBAL_ASYNC_QUERIES_JWT_COOKIE_NAME = \"async-token-prod\"","handlingStrategy":"try-catch","validationCode":"import jwt\ndef token_is_decodable(token: str, secret: str) -> bool:\n    try:\n        jwt.decode(token, secret, algorithms=[\"HS256\"])\n        return True\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"except AsyncQueryTokenException as ex:\n    if \"Failed to parse token\" in str(ex):\n        # instruct client to clear cookies / re-login; check server log for jwt reason\n        return response_401()","preventionTips":["Use per-environment cookie names to avoid cross-instance cookie collisions.","Rotate secrets with a forced logout so stale cookies are never decoded.","Watch the 'Parse jwt failed' warning log to distinguish expiry vs signature failures."],"tags":["async-queries","jwt","authentication","cookies","configuration"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}