{"record":{"id":"687d4169a1cc629d","repo":"apache/superset","slug":"an-unexpected-error-occurred","errorCode":null,"errorMessage":"An unexpected error occurred","messagePattern":"An unexpected error occurred","errorType":"exception","errorClass":"ValidatorSQLUnexpectedError","httpStatus":422,"severity":"error","filePath":"superset/commands/database/validate_sql.py","lineNumber":67,"sourceCode":"class ValidateSQLCommand(BaseCommand):\n    def __init__(self, model_id: int, data: dict[str, Any]):\n        self._properties = data.copy()\n        self._model_id = model_id\n        self._model: Optional[Database] = None\n        self._validator: Optional[type[BaseSQLValidator]] = None\n\n    def run(self) -> list[dict[str, Any]]:\n        \"\"\"\n        Validates a SQL statement\n\n        :return: A List of SQLValidationAnnotation\n        :raises: DatabaseNotFoundError, NoValidatorConfigFoundError\n          NoValidatorFoundError, ValidatorSQLUnexpectedError, ValidatorSQLError\n          ValidatorSQL400Error\n        \"\"\"\n        self.validate()\n        if not self._validator or not self._model:\n            raise ValidatorSQLUnexpectedError()\n        sql = self._properties[\"sql\"]\n        catalog = self._properties.get(\"catalog\")\n        schema = self._properties.get(\"schema\")\n        template_params = self._properties.get(\"template_params\") or {}\n\n        # Check access before rendering the Jinja template (mirrors the SQL\n        # Lab execute path).\n        security_manager.raise_for_access(\n            database=self._model,\n            sql=sql,\n            catalog=catalog,\n            schema=schema,\n            template_params=template_params,\n            force_dataset_match=True,\n        )\n\n        try:\n            # Render Jinja templates to handle template syntax before","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/database/validate_sql.py#L49-L85","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["If you subclass ValidatorSQLCommand, call super().validate() (or replicate its model+validator lookup) so _model and _validator are populated before run().","If invoking the command programmatically, always call command.validate() before command.run(), or use the REST endpoint which does this for you.","Verify the database id still exists (DatabaseDAO.find_by_id) if you suspect concurrent deletion, then retry with a valid database_id.","Check logs for the preceding DatabaseNotFound/NoValidator errors — an unexpected error here usually masks an earlier swallowed exception."],"exampleFix":"// before\ncmd = ValidatorSQLCommand(model_id=1, properties={\"sql\": \"SELECT 1\"})\ncmd.run()  # ValidatorSQLUnexpectedError: validate() never ran\n\n// after\ncmd = ValidatorSQLCommand(model_id=1, properties={\"sql\": \"SELECT 1\"})\ncmd.validate()  # raises the precise error if db/validator missing\ncmd.run()","handlingStrategy":"validation","validationCode":"# Before invoking the command, confirm both preconditions it asserts\nfrom superset.databases.dao import DatabaseDAO\nfrom superset.sql_validators import get_validator_by_name\nfrom flask import current_app\n\ndef can_validate(database_id: int) -> bool:\n    model = DatabaseDAO.find_by_id(database_id)\n    if model is None:\n        return False\n    by_engine = current_app.config[\"SQL_VALIDATORS_BY_ENGINE\"]\n    name = by_engine.get(model.db_engine_spec.engine)\n    return name is not None and get_validator_by_name(name) is not None","typeGuard":null,"tryCatchPattern":"from superset.commands.database.exceptions import ValidatorSQLUnexpectedError\ntry:\n    cmd = ValidatorSQLCommand(model_id, properties)\n    cmd.validate()\n    cmd.run()\nexcept ValidatorSQLUnexpectedError:\n    # invariant break: log state (validator, model id) and fail loudly — do not mask as user error\n    logger.exception(\"validate_sql invariant broken\", extra={\"model_id\": model_id})\n    raise","preventionTips":["Never call command.run() without command.validate() when using command classes directly.","In subclasses, always call super().validate() so _model and _validator are populated.","Treat this error as a bug report, not an input error — capture a traceback in monitoring."],"tags":["sql-validation","internal-invariant","flask-api"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}