crewAIInc/crewAI · error · ValueError
NL2SQLTool is configured in read-only mode and blocked a '{c
Error message
NL2SQLTool is configured in read-only mode and blocked a '{command}' statement. To allow write operations set allow_dml=True or CREWAI_NL2SQL_ALLOW_DML=true. What it means
The core read-only guard: if the first keyword of a (non-CTE, non-EXPLAIN-resolved) statement is in _WRITE_COMMANDS (INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, TRUNCATE, ...) and allow_dml=False, NL2SQLTool raises ValueError telling you to set allow_dml=True or CREWAI_NL2SQL_ALLOW_DML=true. With allow_dml=True it only logs a warning and executes.
Source
Thrown at lib/crewai-tools/src/crewai_tools/tools/nl2sql/nl2sql_tool.py:374
f"operations set allow_dml=True or "
f"CREWAI_NL2SQL_ALLOW_DML=true."
)
logger.warning(
"NL2SQLTool: executing '%s' after CTE because allow_dml=True.",
main_cmd,
)
elif main_cmd not in _READ_ONLY_COMMANDS:
if not self.allow_dml:
raise ValueError(
f"NL2SQLTool blocked an unrecognised SQL command '{main_cmd}' "
f"after a CTE. Only {sorted(_READ_ONLY_COMMANDS)} are allowed "
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."
)
@staticmethodView on GitHub (pinned to 754d7323be)
Solutions
- If the write is intended: NL2SQLTool(db_uri=..., allow_dml=True) or export CREWAI_NL2SQL_ALLOW_DML=true (env var also overrides at runtime).
- If not intended: fix the agent prompt to forbid writes and re-run with a SELECT.
- Point writes at a separate tool instance/connection with a write-capable, low-privilege DB user instead of flipping the shared tool.
Example fix
# before
tool = NL2SQLTool(db_uri=uri)
tool._run("DELETE FROM sessions WHERE expired") # ValueError
# after
tool = NL2SQLTool(db_uri=uri, allow_dml=True)
tool._run("DELETE FROM sessions WHERE expired") Defensive patterns
Strategy: validation
Validate before calling
WRITE_CMDS = {"INSERT", "UPDATE", "DELETE", "DROP", "ALTER", "CREATE", "TRUNCATE", "MERGE", "REPLACE"}
def first_keyword(sql: str) -> str:
return sql.strip().lstrip("(").split()[0].upper().rstrip(";") if sql.strip() else ""
if not tool.allow_dml and first_keyword(sql) in WRITE_CMDS:
raise PermissionError(f"write statement {first_keyword(sql)} not permitted") Type guard
def is_read_only_sql(sql: str) -> bool:
kw = sql.strip().lstrip("(").split()[0].upper().rstrip(";") if sql.strip() else ""
return kw in {"SELECT", "SHOW", "DESCRIBE", "EXPLAIN"} Try / catch
try:
tool._run(sql)
except ValueError as e:
if "read-only mode" in str(e):
if is_authorized_write(sql):
result = write_tool._run(sql) # allow_dml=True instance
else:
raise Prevention
- Validate the leading SQL keyword against the allowlist before calling the tool.
- Use a dedicated write tool backed by a low-privilege DB user instead of flipping allow_dml.
- Set agent prompts to generate SELECT-only queries for read-only tools.
When it happens
Trigger: Calling the tool with any plain write statement ('DELETE FROM users', "UPDATE t SET x=1", 'DROP TABLE t', 'INSERT INTO ...') in the default read-only configuration.
Common situations: Agents asked to 'clean up the table'; prompts that encourage DDL; shared demo tools left read-only on purpose while a script tries to seed data; forgetting that allow_dml defaults to False.
Related errors
- NL2SQLTool blocked a multi-statement query in read-only mode
- NL2SQLTool is configured in read-only mode and blocked a '{m
- NL2SQLTool is configured in read-only mode and blocked a wri
- NL2SQLTool blocked an unrecognised SQL command '{main_cmd}'
- NL2SQLTool blocked an unrecognised SQL command '{command}'.
AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15).
Data as JSON: /api/errors/aebf364fcc92dddb.
Report an issue: GitHub.