{"record":{"id":"9cb59dee7d9c5332","repo":"n8n-io/n8n","slug":"sql-operation-blockedkeyword-is-not-allowed","errorCode":null,"errorMessage":"SQL operation \"${blockedKeyword}\" is not allowed","messagePattern":"SQL operation \"(.+?)\" is not allowed","errorType":"exception","errorClass":"NodeOperationError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts","lineNumber":165,"sourceCode":"\t\t\t\tappDataSource: dataSource,\n\t\t\t\tincludesTables: includedTablesArray.length > 0 ? includedTablesArray : undefined,\n\t\t\t\tignoreTables: ignoredTablesArray.length > 0 ? ignoredTablesArray : undefined,\n\t\t\t\tsampleRowsInTableInfo: includedSampleRows ?? 3,\n\t\t\t});\n\n\t\t\tconst toolkit = new SqlToolkit(dbInstance, model);\n\t\t\tconst agentExecutor = createSqlAgent(model, toolkit, agentOptions);\n\n\t\t\tagentExecutor.tools = agentExecutor.tools.map((tool) => {\n\t\t\t\tif (tool.name !== 'query-sql') return tool;\n\n\t\t\t\treturn new DynamicTool({\n\t\t\t\t\tname: tool.name,\n\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\tfunc: async (sqlInput: string) => {\n\t\t\t\t\t\tconst blockedKeyword = detectBlockedSqlKeyword(sqlInput);\n\t\t\t\t\t\tif (blockedKeyword) {\n\t\t\t\t\t\t\tthrow new NodeOperationError(\n\t\t\t\t\t\t\t\tthis.getNode(),\n\t\t\t\t\t\t\t\t`SQL operation \"${blockedKeyword}\" is not allowed`,\n\t\t\t\t\t\t\t\t{ itemIndex: i },\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn String(await tool.invoke(sqlInput));\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tconst memory = (await this.getInputConnectionData(NodeConnectionTypes.AiMemory, 0)) as\n\t\t\t\t| BaseChatMemory\n\t\t\t\t| undefined;\n\n\t\t\tagentExecutor.memory = memory;\n\n\t\t\tlet chatHistory = '';\n\t\t\tif (memory) {","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts#L147-L183","documentation":"Safety guard thrown by the SQL Agent when the LLM-generated SQL contains a destructive or mutating keyword. The detectBlockedSqlKeyword function strips comments and string literals, then matches any of INSERT, UPDATE, DELETE, DROP, TRUNCATE, ALTER, CREATE, REPLACE, MERGE as whole words. The agent is intentionally read-only.","triggerScenarios":"The model emits a tool call to 'query-sql' whose SQL argument contains any blocked keyword outside of a string literal or comment. e.g. the LLM tries DROP TABLE users or UPDATE accounts SET ....","commonSituations":"User asks the agent to 'clean up' or 'modify' records; the LLM hallucinates a schema fix involving DDL; prompt encourages write operations; few-shot examples leak mutating SQL.","solutions":["Reframe the prompt to make clear only read-only SELECT queries are allowed.","Connect a read-only database user so even if the guard were bypassed the DB rejects writes.","If writes are genuinely required, use a dedicated SQL node (Postgres/MySQL) instead of the SQL Agent."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Defensive check at the workflow authoring layer: warn users the agent is read-only.\n// detectBlockedSqlKeyword is the runtime guard — pair it with a read-only DB user.\nconst dbUser = credentialData.username;\nif (!dbUser || dbUser.endsWith('_readonly') === false) {\n  console.warn('SQL Agent should connect with a read-only database user.');\n}","typeGuard":"function isReadOnlySql(sql: string): boolean {\n  return detectBlockedSqlKeyword(sql) === undefined;\n}","tryCatchPattern":"try {\n  return String(await tool.invoke(sqlInput));\n} catch (e) {\n  if (e instanceof NodeOperationError && /SQL operation/.test(e.message)) {\n    // Return a structured error so the agent can recover and try a SELECT instead\n    return JSON.stringify({ error: 'blocked-sql-operation', sql: sqlInput });\n  }\n  throw e;\n}","preventionTips":["Connect the SQL Agent with a read-only database credential so writes are impossible at the DB level.","Phrase prompts to request analysis, not modification.","Document to end-users that the agent is read-only."],"tags":["langchain","agent","sql","security","guardrail","read-only"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}