n8n-io/n8n · error

Invalid PgVectorStore table name "${String(this.tableName)}"

Error message

Invalid PgVectorStore table name "${String(this.tableName)}": must match ${IDENTIFIER_PATTERN}

What it means

Thrown in the PgVectorStore constructor when tableName fails the IDENTIFIER_PATTERN `/^[A-Za-z_][A-Za-z0-9_]*$/`. The table name is interpolated directly into SQL (it cannot be a bind parameter), so it is locked to a safe identifier grammar to prevent SQL injection and syntax errors. Schema-qualified names, dots, hyphens, spaces, digits-first, and quoted identifiers all fail. This throws at construction time, before any DB connection.

Source

Thrown at packages/@n8n/agents/src/vector-stores/postgres.ts:58

 * ```typescript
 * const store = new PgVectorStore('product-docs', {
 *   connectionString: 'postgresql://user:pass@localhost:5432/db',
 *   tableName: 'product_docs',
 * });
 * ```
 */
export class PgVectorStore extends BaseVectorStore<PgVectorStoreOptions> {
	private readonly tableName: string;

	private pool?: Pool;

	private iterativeScanSupportedPromise?: Promise<boolean>;

	constructor(name: string, options: PgVectorStoreOptions) {
		super(name, options);
		this.tableName = options.tableName;
		if (typeof this.tableName !== 'string' || !IDENTIFIER_PATTERN.test(this.tableName)) {
			throw new Error(
				`Invalid PgVectorStore table name "${String(this.tableName)}": must match ${IDENTIFIER_PATTERN}`,
			);
		}
	}

	async upsert(records: VectorRecord[]): Promise<void> {
		if (records.length === 0) return;

		const pool = await this.getPool();
		const values: string[] = [];
		const params: unknown[] = [];
		records.forEach((record, index) => {
			const base = index * 4;
			values.push(`($${base + 1}, $${base + 2}, $${base + 3}, $${base + 4}::vector)`);
			params.push(
				record.id,
				record.content,
				JSON.stringify(record.metadata),

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Pass a bare identifier matching the pattern: letters/underscore start, then letters/digits/underscore (e.g. `product_docs`).
  2. If your table is in a non-public schema, connect with a role/search_path that defaults to that schema and pass only the table name.
  3. Rename the Postgres table to a valid identifier if you control the schema.
  4. Validate the table name from config with the same regex before constructing the store.

Example fix

// before
new PgVectorStore('docs', { connectionString, tableName: 'public.product-docs' });

// after
new PgVectorStore('docs', { connectionString, tableName: 'product_docs' });
Defensive patterns

Strategy: validation

Validate before calling

const IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;

function assertValidTableName(name: unknown): string {
  if (typeof name !== 'string' || !IDENTIFIER_PATTERN.test(name)) {
    throw new Error(`Invalid table name "${String(name)}": must match ${IDENTIFIER_PATTERN}`);
  }
  return name;
}

new PgVectorStore('docs', { connectionString, tableName: assertValidTableName(process.env.PG_TABLE) });

Type guard

function isValidPgIdentifier(name: string): boolean {
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
}

if (!isValidPgIdentifier(config.table)) throw new Error('Bad table name');

Prevention

When it happens

Trigger: Passing `tableName: 'public.product_docs'` (dot), `'product-docs'` (hyphen), `'2docs'` (digit-first), `'Product Docs'` (space/uppercase+space — uppercase alone is allowed but space is not), `'"docs"'` (quotes), or an empty string.

Common situations: Using a schema-qualified Postgres table name; copying a table name with hyphens from a separate DB tool; environment/config injection of an unvalidated table name; multi-tenant setups wanting `tenant_123.docs`.

Related errors


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