apache/superset · error · AsyncQueryTokenException

Failed to parse token

Error message

Failed to parse token

What it means

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.

Source

Thrown at superset/async_events/async_query_manager.py:279

        ).hexdigest()
        return f"guest-{digest}"

    def parse_channel_id_from_request(self, req: Request) -> str:
        # pylint: disable=import-outside-toplevel
        from superset import security_manager

        if guest_user := security_manager.get_current_guest_user_if_guest():
            return self.get_guest_user_channel_id(guest_user)

        token = req.cookies.get(self._jwt_cookie_name)
        if not token:
            raise AsyncQueryTokenException("Token not preset")

        try:
            return jwt.decode(token, self._jwt_secret, algorithms=["HS256"])["channel"]
        except Exception as ex:
            logger.warning("Parse jwt failed", exc_info=True)
            raise AsyncQueryTokenException("Failed to parse token") from ex

    def init_job(self, channel_id: str, user_id: Optional[int]) -> dict[str, Any]:
        job_id = str(uuid.uuid4())
        self._register_cancellable_job(job_id, channel_id, user_id)
        return build_job_metadata(
            channel_id, job_id, user_id, status=self.STATUS_PENDING
        )

    def _job_registry_key(self, job_id: str) -> str:
        return f"{self._stream_prefix}{self._JOB_REGISTRY_PREFIX}{job_id}"

    def _register_cancellable_job(
        self, job_id: str, channel_id: str, user_id: Optional[int]
    ) -> None:
        """
        Persist the identity a later cancel request must match. Keyed by
        ``job_id`` (also the Celery task id — see ``submit_chart_data_job``) so
        the cancel endpoint can authorize the caller against the job's original

View on GitHub (pinned to f4587218dd)

Solutions

  1. Have the user clear the Superset cookies (or log out/in) so a fresh token is issued with the current secret.
  2. If the secret was rotated, ensure ALL webserver and worker pods share the new value; inconsistent secrets across replicas cause intermittent failures.
  3. Give each environment a distinct GLOBAL_ASYNC_QUERIES_JWT_COOKIE_NAME to avoid cookie collisions between instances on the same domain.
  4. Check the webserver log line 'Parse jwt failed' for the underlying jwt error (ExpiredSignature, InvalidSignature, DecodeError) to pick the right fix.

Example fix

# deployment fix: unique cookie names per environment
# before
GLOBAL_ASYNC_QUERIES_JWT_COOKIE_NAME = "async-token"
# (dev and prod on same domain overwrite each other's cookies)

# after
GLOBAL_ASYNC_QUERIES_JWT_COOKIE_NAME = "async-token-prod"
Defensive patterns

Strategy: try-catch

Validate before calling

import jwt
def token_is_decodable(token: str, secret: str) -> bool:
    try:
        jwt.decode(token, secret, algorithms=["HS256"])
        return True
    except Exception:
        return False

Try / catch

except AsyncQueryTokenException as ex:
    if "Failed to parse token" in str(ex):
        # instruct client to clear cookies / re-login; check server log for jwt reason
        return response_401()

Prevention

When it happens

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

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of apache/superset@f4587218dd (2026-08-14). Data as JSON: /api/errors/b1adc1a42359c48f. Report an issue: GitHub.