n8n-io/n8n · error · ReturningStatementNotSupportedError

OUTPUT or RETURNING clause only supported by Microsoft SQL S

Error message

OUTPUT or RETURNING clause only supported by Microsoft SQL Server or PostgreSQL or MariaDB databases.

What it means

InsertQueryBuilder.returning() has the same guard as the delete variant but checks isReturningSqlSupported('insert'). It throws ReturningStatementNotSupportedError before building the INSERT, so no statement reaches the database. Drivers that support RETURNING/OUTPUT (Postgres family, SQL Server, modern SQLite) pass; others (MySQL, Oracle, SAP, Mongo) fail.

Source

Thrown at packages/@n8n/typeorm/src/query-builder/InsertQueryBuilder.ts:253

	/**
	 * Optional returning/output clause.
	 * Returning is a SQL string containing returning statement.
	 */
	returning(returning: string): this;

	/**
	 * Optional returning/output clause.
	 */
	returning(returning: string | string[]): this;

	/**
	 * Optional returning/output clause.
	 */
	returning(returning: string | string[]): this {
		// not all databases support returning/output cause
		if (!this.connection.driver.isReturningSqlSupported('insert')) {
			throw new ReturningStatementNotSupportedError();
		}

		this.expressionMap.returning = returning;
		return this;
	}

	/**
	 * Indicates if entity must be updated after insertion operations.
	 * This may produce extra query or use RETURNING / OUTPUT statement (depend on database).
	 * Enabled by default.
	 */
	updateEntity(enabled: boolean): this {
		this.expressionMap.updateEntity = enabled;
		return this;
	}

	/**
	 * Adds additional ON CONFLICT statement supported in postgres and cockroach.

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Remove .returning() and rely on .generatedMap or the returned identifer after insert (insert().values().execute().identifiers).
  2. Branch on driver.isReturningSqlSupported('insert') before calling .returning().
  3. Use repository.save() which handles id retrieval per-driver instead of a raw insert with returning.
  4. Switch to Postgres/SQLite if RETURNING-on-insert is a hard requirement.

Example fix

// before
const { raw } = await dataSource.createQueryBuilder()
  .insert().into(User).values({ email })
  .returning(['id']).execute(); // throws on MySQL

// after
const res = await dataSource.createQueryBuilder()
  .insert().into(User).values({ email }).execute();
const id = res.identifiers[0]?.id;
Defensive patterns

Strategy: validation

Validate before calling

function supportsInsertReturning(ds: DataSource): boolean {
  return ds.driver.isReturningSqlSupported('insert');
}
if (!supportsInsertReturning(dataSource)) { /* use identifiers from execute() */ }

Type guard

function supportsInsertReturning(driver: import('../driver/Driver').Driver): boolean {
  return driver.isReturningSqlSupported('insert');
}

Try / catch

try {
  return qb.insert().into(E).values(v).returning(cols).execute();
} catch (e) {
  if (e instanceof ReturningStatementNotSupportedError) { /* use execute().identifiers */ }
  throw e;
}

Prevention

When it happens

Trigger: Calling .insert().into(Entity).values(...).returning(['id']).execute() on a driver where isReturningSqlSupported('insert') is false. Also surfaces when updateEntity behaviour (enabled by default) is forced to use RETURNING on an unsupported driver.

Common situations: Migrating a Postgres-only insert-and-get-id flow to MySQL/MariaDB. CI matrix tests running SQLite where RETURNING is conditionally supported. Library code that unconditionally requests returning primary keys.

Related errors


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