getredash/redash · error · PermissionError

Query id {} not found.

Error message

Query id {} not found.

What it means

Raised by _load_query (redash/query_runner/query_results.py:49) as a PermissionError when the requested query exists but belongs to a different organization than the requesting user. Redash is multi-tenant by org_id, and cross-org access is treated as 'not found' to avoid leaking the existence of queries in other tenants.

Source

Thrown at redash/query_runner/query_results.py:49

def extract_query_params(query):
    return re.findall(r"(?:join|from)\s+param_query_(\d+)_{([^}]+)}", query, re.IGNORECASE)


def extract_query_ids(query):
    queries = re.findall(r"(?:join|from)\s+query_(\d+)", query, re.IGNORECASE)
    return [int(q) for q in queries]


def extract_cached_query_ids(query):
    queries = re.findall(r"(?:join|from)\s+cached_query_(\d+)", query, re.IGNORECASE)
    return [int(q) for q in queries]


def _load_query(user, query_id):
    query = models.Query.get_by_id(query_id)

    if user.org_id != query.org_id:
        raise PermissionError("Query id {} not found.".format(query.id))

    # TODO: this duplicates some of the logic we already have in the redash.handlers.query_results.
    # We should merge it so it's consistent.
    if not has_access(query.data_source, user, view_only):
        raise PermissionError("You do not have access to query id {}.".format(query.id))

    return query


def replace_query_parameters(query_text, params):
    qs = parse_qs(params)
    for key, value in qs.items():
        query_text = query_text.replace("{{{{{my_key}}}}}".format(my_key=key), value[0])
    return query_text


def get_query_results(user, query_id, bring_from_cache, params=None):
    query = _load_query(user, query_id)

View on GitHub (pinned to ca79fe988d)

Solutions

  1. Verify the query id belongs to the same Redash instance/account the user is on (open /queries/<id> in the UI)
  2. Update the hardcoded query id in the calling query/python script to one from the current org
  3. In tests, create both the user and the query under the same org (models.Query created with the user's org)

Example fix

# before (test)
user = factory.create_user(org=factory.create_org())
query = factory.create_query(org=self.org)
# after
user = factory.create_user(org=self.org)
query = factory.create_query(org=self.org)
Defensive patterns

Strategy: try-catch

Validate before calling

query = models.Query.get_by_id(query_id)
if query is None or query.org_id != user.org_id:
    return error_response(404, 'query not found')

Try / catch

try:
    results = get_query_results(user, qid, bring_from_cache)
except PermissionError as e:
    return error_response(404, str(e))  # treat cross-org as not found
except Exception as e:
    return error_response(400, str(e))

Prevention

When it happens

Trigger: Calling get_query_results(user, query_id, ...) or the Python `query_results` query runner with a query id whose org_id differs from user.org_id — e.g. a query id copied from another Redash instance or another account's URL.

Common situations: Copy-pasting a query id or full query URL between staging/production or between two separate Redash installations; stale hardcoded query ids after a data migration or org restructure; tests that create users and queries under different orgs.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of getredash/redash@ca79fe988d (2026-08-28). Data as JSON: /api/errors/be0c7a7d1fd1b6de. Report an issue: GitHub.