apache/superset · error · ValidatorSQLUnexpectedError

An unexpected error occurred

Error message

An unexpected error occurred

What it means

ValidatorSQLUnexpectedError (HTTP 422, message 'An unexpected error occurred') is raised at the top of ValidatorSQLCommand.run() when the command's invariants are broken: after validate() has run, either self._validator or self._model is None. Under normal operation validate() would already have raised DatabaseNotFoundError, NoValidatorConfigFoundError, or NoValidatorFoundError, so reaching run() with either field unset indicates an internal state or subclassing bug rather than a user input problem.

Source

Thrown at superset/commands/database/validate_sql.py:67

class ValidateSQLCommand(BaseCommand):
    def __init__(self, model_id: int, data: dict[str, Any]):
        self._properties = data.copy()
        self._model_id = model_id
        self._model: Optional[Database] = None
        self._validator: Optional[type[BaseSQLValidator]] = None

    def run(self) -> list[dict[str, Any]]:
        """
        Validates a SQL statement

        :return: A List of SQLValidationAnnotation
        :raises: DatabaseNotFoundError, NoValidatorConfigFoundError
          NoValidatorFoundError, ValidatorSQLUnexpectedError, ValidatorSQLError
          ValidatorSQL400Error
        """
        self.validate()
        if not self._validator or not self._model:
            raise ValidatorSQLUnexpectedError()
        sql = self._properties["sql"]
        catalog = self._properties.get("catalog")
        schema = self._properties.get("schema")
        template_params = self._properties.get("template_params") or {}

        # Check access before rendering the Jinja template (mirrors the SQL
        # Lab execute path).
        security_manager.raise_for_access(
            database=self._model,
            sql=sql,
            catalog=catalog,
            schema=schema,
            template_params=template_params,
            force_dataset_match=True,
        )

        try:
            # Render Jinja templates to handle template syntax before

View on GitHub (pinned to f4587218dd)

Solutions

  1. If you subclass ValidatorSQLCommand, call super().validate() (or replicate its model+validator lookup) so _model and _validator are populated before run().
  2. If invoking the command programmatically, always call command.validate() before command.run(), or use the REST endpoint which does this for you.
  3. Verify the database id still exists (DatabaseDAO.find_by_id) if you suspect concurrent deletion, then retry with a valid database_id.
  4. Check logs for the preceding DatabaseNotFound/NoValidator errors — an unexpected error here usually masks an earlier swallowed exception.

Example fix

// before
cmd = ValidatorSQLCommand(model_id=1, properties={"sql": "SELECT 1"})
cmd.run()  # ValidatorSQLUnexpectedError: validate() never ran

// after
cmd = ValidatorSQLCommand(model_id=1, properties={"sql": "SELECT 1"})
cmd.validate()  # raises the precise error if db/validator missing
cmd.run()
Defensive patterns

Strategy: validation

Validate before calling

# Before invoking the command, confirm both preconditions it asserts
from superset.databases.dao import DatabaseDAO
from superset.sql_validators import get_validator_by_name
from flask import current_app

def can_validate(database_id: int) -> bool:
    model = DatabaseDAO.find_by_id(database_id)
    if model is None:
        return False
    by_engine = current_app.config["SQL_VALIDATORS_BY_ENGINE"]
    name = by_engine.get(model.db_engine_spec.engine)
    return name is not None and get_validator_by_name(name) is not None

Try / catch

from superset.commands.database.exceptions import ValidatorSQLUnexpectedError
try:
    cmd = ValidatorSQLCommand(model_id, properties)
    cmd.validate()
    cmd.run()
except ValidatorSQLUnexpectedError:
    # invariant break: log state (validator, model id) and fail loudly — do not mask as user error
    logger.exception("validate_sql invariant broken", extra={"model_id": model_id})
    raise

Prevention

When it happens

Trigger: Calling run() on a ValidatorSQLCommand whose validate() was skipped or overridden to a no-op; subclassing the command and not populating _model/_validator; a race where the validator registry or database row disappears between validate() and run() (e.g., concurrent deletion of the Database row).

Common situations: Custom subclasses of the validate_sql command that override validate() without calling super(); calling the internal command API directly (not via the REST endpoint) in scripts/tests without invoking validate() first; code upgrades that changed the command lifecycle.

Related errors


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