apache/superset · error · AsyncQueryTokenException

Not authorized to cancel this job

Error message

Not authorized to cancel this job

What it means

AsyncQueryTokenException raised during cancellation when the registry record exists but its stored channel_id or user_id does not match the caller's. Each job's cancel record is scoped to the channel and user that created it, so only the owner may cancel; a mismatch means a different user (or a different session/channel for the same user) is attempting the cancellation. This is an authorization check, not a token-format issue, despite the exception class name.

Source

Thrown at superset/async_events/async_query_manager.py:441

        owner. The terminal ``STATUS_CANCELLED`` event is emitted here rather
        than by the worker, which never runs for a task revoked while it was
        still queued; the worker only logs the cancellation it is told about
        through the flag, so a job still gets exactly one terminal event.

        :raises AsyncQueryJobException: the job is unknown or already terminal
        :raises AsyncQueryTokenException: the caller does not own the job
        """
        if not self._cache:
            raise CacheBackendNotInitialized("Cache backend not initialized")

        key = self._job_registry_key(job_id)
        raw = self._cache.get(key)
        if raw is None:
            raise AsyncQueryJobException("Job not found or already completed")

        record = json.loads(raw)
        if record.get("channel_id") != channel_id or record.get("user_id") != user_id:
            raise AsyncQueryTokenException("Not authorized to cancel this job")

        # Flag before revoking so the worker's timeout handler, which may fire
        # almost immediately, reliably sees the cancellation. Write only if the
        # key still exists (``xx``): if the job finished and cleared its record
        # between the read above and here, don't recreate a stale record or
        # revoke a task that is already gone — report it as not found instead.
        flagged = self._cache.set(
            key,
            json.dumps({**record, "cancelled": True}),
            ex=self._jwt_expiration_seconds or None,
            xx=True,
        )
        if not flagged:
            raise AsyncQueryJobException("Job not found or already completed")

        # pylint: disable=import-outside-toplevel
        from superset.extensions import celery_app

View on GitHub (pinned to f4587218dd)

Solutions

  1. Ensure the cancel request uses the channel_id and user_id from the exact job metadata returned when the job was created — do not reconstruct them from the current session alone.
  2. Clear cached job metadata on logout so a new user's session never reuses the previous user's channel.
  3. Treat AsyncQueryTokenException from cancel as 403 in the API layer: log and inform the user they do not own the job.

Example fix

# before
job = load_last_job_from_localstorage()  # may belong to previous user
manager.cancel_job(job["job_id"], current_channel_id, current_user_id)

# after
job = load_last_job_from_localstorage()
if job and job["user_id"] == current_user_id:
    manager.cancel_job(job["job_id"], job["channel_id"], job["user_id"])
Defensive patterns

Strategy: validation

Validate before calling

job = get_job_metadata()  # exactly what the create call returned
owns_job = (
    job is not None
    and job.get("channel_id") == current_channel_id
    and job.get("user_id") == current_user_id
)
if not owns_job:
    skip_cancel("not job owner")

Try / catch

except AsyncQueryTokenException as ex:
    if "Not authorized" in str(ex):
        return response_403()  # owner mismatch, do not retry

Prevention

When it happens

Trigger: User A calling cancel with User B's job_id (even if both are logged in); the same user cancelling from a session whose channel id differs (e.g. cookie re-issued after re-login, embedded vs standard session); a forged request replaying another channel's metadata with a guessed job_id.

Common situations: Multi-tab/multi-session usage where job metadata from one tab is used in another; embedded dashboard guest sessions mixing with the owner's session; frontend bugs that cache job metadata across user logins (logout then login as another user without clearing state).

Related errors


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