apache/superset · error · DatasourceNotFoundValidationError

Datasource does not exist

Error message

Datasource does not exist

What it means

DatasourceNotFoundValidationError raised in UpdateRLSRuleCommand.validate when the payload includes 'tables' but fewer SqlaTable rows match self._tables than requested — at least one dataset id in the update does not exist. Access (raise_for_datasource_access) is checked only after all ids resolve.

Source

Thrown at superset/commands/security/update.py:79

            ) from ex
        return updated_model

    def validate(self) -> None:
        self._model = RLSDAO.find_by_id(int(self._model_id))
        if not self._model:
            raise RLSRuleNotFoundError()

        # Datasource access is validated before revealing whether the
        # requested name is already in use, so an unauthorized caller can't
        # use the duplicate-name response to enumerate rule names.
        if "tables" in self._properties:
            tables = (
                db.session.query(SqlaTable)
                .filter(SqlaTable.id.in_(self._tables))  # type: ignore[attr-defined]
                .all()
            )
            if len(tables) != len(self._tables):
                raise DatasourceNotFoundValidationError()
            raise_for_datasource_access(tables)
            self._properties["tables"] = tables
        else:
            # A partial update that omits ``tables`` still mutates the rule, so
            # enforce datasource access against the rule's existing tables to
            # avoid letting a caller edit a rule bound to datasources they
            # cannot access.
            raise_for_datasource_access(self._model.tables)

        name = self._properties.get("name")
        if name and not RLSDAO.validate_uniqueness(name, self._model.id):
            raise ValidationError(
                {"name": [_("A rule with this name already exists.")]}
            )

        # Only resolve and overwrite the relationships that are actually present
        # in the request body. A partial update (e.g. changing only the name)
        # must leave the rule's existing tables/subjects bindings untouched

View on GitHub (pinned to f4587218dd)

Solutions

  1. Resolve dataset ids at request time via GET /api/v1/dataset/ (filter by table_name/uuid) instead of hard-coding
  2. Drop the stale id from the 'tables' array and retry
  3. Recreate the missing dataset if it should still exist

Example fix

# before
client.put(rls_url, json={'tables': [7, 999]})  # 999 gone

# after
ds_id = client.get('/api/v1/dataset/?q=(table_name:eq:my_table)').json()['result'][0]['id']
client.put(rls_url, json={'tables': [7, ds_id]})
Defensive patterns

Strategy: validation

Validate before calling

known = {d['id'] for d in client.get('/api/v1/dataset/').json()['result']}
assert set(props['tables']) <= known

Try / catch

from superset.commands.exceptions import DatasourceNotFoundValidationError
try:
    UpdateRLSRuleCommand(rid, props).run()
except DatasourceNotFoundValidationError:
    props['tables'] = resolve_dataset_ids_by_name(props['tables'])
    UpdateRLSRuleCommand(rid, props).run()

Prevention

When it happens

Trigger: PUT /api/v1/rowlevelsecurity/{id} with 'tables' containing a deleted/invalid dataset id; rebinding a rule to datasets whose ids changed after a metadata migration.

Common situations: Updating rule bindings with ids copied from another environment; datasets dropped by a cleanup job while the rule edit was in flight.

Related errors


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