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
- 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.
- Clear cached job metadata on logout so a new user's session never reuses the previous user's channel.
- 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
- Always cancel with the job metadata returned at creation time, not reconstructed session values.
- Purge cached job metadata on logout to prevent cross-user leakage.
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
- Job not found or already completed
- Unsupported cache backend configuration
- Cache backends (CACHE_CONFIG, DATA_CACHE_CONFIG) must be con
- Please provide a JWT secret at least 32 bytes long
- Token not preset
AI-assisted analysis of apache/superset@f4587218dd (2026-08-14).
Data as JSON: /api/errors/92e07b7585475122.
Report an issue: GitHub.