crewAIInc/crewAI · error · ValueError

NL2SQLTool blocked an unrecognised SQL command '{command}'.

Error message

NL2SQLTool blocked an unrecognised SQL command '{command}'. Only {sorted(_READ_ONLY_COMMANDS)} are allowed in read-only mode.

What it means

Deny-by-default allowlist for unknown commands: if a statement's first keyword is neither in _WRITE_COMMANDS nor _READ_ONLY_COMMANDS (SELECT, SHOW, DESCRIBE, EXPLAIN), read-only mode raises ValueError listing the allowed set. This catches dialect utilities (SET, PRAGMA, VACUUM, CALL, GRANT, ...) and typos that would otherwise slip past a write-only blocklist.

Source

Thrown at lib/crewai-tools/src/crewai_tools/tools/nl2sql/nl2sql_tool.py:386

                            f"in read-only mode."
                        )
            return

        if command in _WRITE_COMMANDS:
            if not self.allow_dml:
                raise ValueError(
                    f"NL2SQLTool is configured in read-only mode and blocked a "
                    f"'{command}' statement. To allow write operations set "
                    f"allow_dml=True or CREWAI_NL2SQL_ALLOW_DML=true."
                )
            logger.warning(
                "NL2SQLTool: executing write statement '%s' because allow_dml=True.",
                command,
            )
        elif command not in _READ_ONLY_COMMANDS:
            # Unknown command — block by default unless DML is explicitly enabled
            if not self.allow_dml:
                raise ValueError(
                    f"NL2SQLTool blocked an unrecognised SQL command '{command}'. "
                    f"Only {sorted(_READ_ONLY_COMMANDS)} are allowed in read-only "
                    f"mode."
                )

    @staticmethod
    def _extract_command(sql_query: str) -> str:
        """Return the uppercased first keyword of *sql_query*."""
        stripped = sql_query.strip().lstrip("(")
        first_token = stripped.split()[0] if stripped.split() else ""
        return first_token.upper().rstrip(";")

    # Schema introspection helpers

    def _fetch_available_tables(self) -> list[dict[str, Any]] | str:
        return self.execute_sql(
            "SELECT table_name FROM information_schema.tables "
            "WHERE table_schema = 'public';"

View on GitHub (pinned to 754d7323be)

Solutions

  1. Check the first keyword of your SQL; correct typos or rephrase to an allowed read statement (SELECT/SHOW/DESCRIBE/EXPLAIN).
  2. Run session or admin commands through a direct SQLAlchemy connection, not NL2SQLTool.
  3. Set allow_dml=True only if you understand it disables all read-only guarding, including this allowlist.

Example fix

# before (blocked: 'SELEC' typo)
tool._run("SELEC * FROM users")

# after
tool._run("SELECT * FROM users")
Defensive patterns

Strategy: validation

Validate before calling

READ_ONLY = {"SELECT", "SHOW", "DESCRIBE", "EXPLAIN"}
WRITE = {"INSERT", "UPDATE", "DELETE", "DROP", "ALTER", "CREATE", "TRUNCATE", "MERGE"}

def classify(sql: str) -> str:
    kw = sql.strip().lstrip("(").split()[0].upper().rstrip(";") if sql.strip() else ""
    return "read" if kw in READ_ONLY else "write" if kw in WRITE else "unknown"

if not tool.allow_dml and classify(sql) == "unknown":
    raise ValueError(f"unknown command will be blocked: {sql[:40]}")

Type guard

def is_known_sql_command(sql: str) -> bool:
    kw = sql.strip().lstrip("(").split()[0].upper().rstrip(";") if sql.strip() else ""
    return kw in {"SELECT", "SHOW", "DESCRIBE", "EXPLAIN"} | {"INSERT", "UPDATE", "DELETE", "DROP", "ALTER", "CREATE", "TRUNCATE", "MERGE"}

Try / catch

try:
    tool._run(sql)
except ValueError as e:
    if "unrecognised SQL command" in str(e):
        sql = repair_keyword_typo(sql)  # e.g. SELEC -> SELECT
        tool._run(sql)

Prevention

When it happens

Trigger: Running 'SET timezone=UTC', 'PRAGMA table_info(t)', 'CALL proc()', 'USE mydb', or a typo like 'SELEC * FROM t' with allow_dml=False.

Common situations: LLM emits session/dialect commands the allowlist does not know; SQLite PRAGMAs; MySQL USE statements; keyword typos from the model.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/3ee5d2c8562e92b5. Report an issue: GitHub.