n8n-io/n8n · error · NodeOperationError

SQL operation "${blockedKeyword}" is not allowed

Error message

SQL operation "${blockedKeyword}" is not allowed

What it means

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.

Source

Thrown at packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/SqlAgent/execute.ts:165

				appDataSource: dataSource,
				includesTables: includedTablesArray.length > 0 ? includedTablesArray : undefined,
				ignoreTables: ignoredTablesArray.length > 0 ? ignoredTablesArray : undefined,
				sampleRowsInTableInfo: includedSampleRows ?? 3,
			});

			const toolkit = new SqlToolkit(dbInstance, model);
			const agentExecutor = createSqlAgent(model, toolkit, agentOptions);

			agentExecutor.tools = agentExecutor.tools.map((tool) => {
				if (tool.name !== 'query-sql') return tool;

				return new DynamicTool({
					name: tool.name,
					description: tool.description,
					func: async (sqlInput: string) => {
						const blockedKeyword = detectBlockedSqlKeyword(sqlInput);
						if (blockedKeyword) {
							throw new NodeOperationError(
								this.getNode(),
								`SQL operation "${blockedKeyword}" is not allowed`,
								{ itemIndex: i },
							);
						}
						return String(await tool.invoke(sqlInput));
					},
				});
			});

			const memory = (await this.getInputConnectionData(NodeConnectionTypes.AiMemory, 0)) as
				| BaseChatMemory
				| undefined;

			agentExecutor.memory = memory;

			let chatHistory = '';
			if (memory) {

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Reframe the prompt to make clear only read-only SELECT queries are allowed.
  2. Connect a read-only database user so even if the guard were bypassed the DB rejects writes.
  3. If writes are genuinely required, use a dedicated SQL node (Postgres/MySQL) instead of the SQL Agent.
Defensive patterns

Strategy: validation

Validate before calling

// Defensive check at the workflow authoring layer: warn users the agent is read-only.
// detectBlockedSqlKeyword is the runtime guard — pair it with a read-only DB user.
const dbUser = credentialData.username;
if (!dbUser || dbUser.endsWith('_readonly') === false) {
  console.warn('SQL Agent should connect with a read-only database user.');
}

Type guard

function isReadOnlySql(sql: string): boolean {
  return detectBlockedSqlKeyword(sql) === undefined;
}

Try / catch

try {
  return String(await tool.invoke(sqlInput));
} catch (e) {
  if (e instanceof NodeOperationError && /SQL operation/.test(e.message)) {
    // Return a structured error so the agent can recover and try a SELECT instead
    return JSON.stringify({ error: 'blocked-sql-operation', sql: sqlInput });
  }
  throw e;
}

Prevention

When it happens

Trigger: 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 ....

Common situations: 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.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/9cb59dee7d9c5332. Report an issue: GitHub.