can1357/oh-my-pi · error

SqlSessionStorage: table name must match ${IDENT_RE.source}

Error message

SqlSessionStorage: table name must match ${IDENT_RE.source} (got ${JSON.stringify(table)})

What it means

SqlSessionStorage interpolates the table name directly into generated SQL, so it validates it against IDENT_RE (identifier-safe characters only) in the constructor and throws if it fails. This prevents SQL injection and syntax errors from table names containing quotes, dashes, spaces, or dots.

Source

Thrown at packages/coding-agent/src/session/sql-session-storage.ts:278

	get table(): string {
		return this.#table;
	}
}

class SqlSessionStorageBackend implements SessionStorageBackend {
	readonly #client: SqlSessionStorageClient;
	readonly #adapter: SqlSessionStorageAdapter;
	readonly #table: string;
	readonly #q: DialectQueries;
	readonly #createTable: boolean;

	constructor(options: SqlSessionStorageOptions) {
		this.#client = options.client;
		this.#adapter = options.adapter ?? detectAdapter(options.client);
		const table = options.table ?? DEFAULT_TABLE;
		if (!IDENT_RE.test(table)) {
			throw new Error(`SqlSessionStorage: table name must match ${IDENT_RE.source} (got ${JSON.stringify(table)})`);
		}
		this.#table = table;
		this.#q = buildQueries(this.#adapter, table);
		this.#createTable = options.createTable !== false;
	}

	get adapter(): SqlSessionStorageAdapter {
		return this.#adapter;
	}

	get table(): string {
		return this.#table;
	}

	async init(): Promise<void> {
		if (this.#createTable) {
			await this.#client.unsafe(this.#q.createTable);
			for (const query of this.#q.addTitleColumns) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Use a plain identifier: letters, digits, underscores only, e.g. 'sessions' or 'omp_sessions_v2'.
  2. Sanitize derived names before passing: replace invalid chars with '_' and ensure it doesn't start with a digit if IDENT_RE requires that.
  3. Omit the option entirely to use DEFAULT_TABLE.
  4. If you need schema-qualified names, connect with a search_path/connection default schema instead.

Example fix

// before
new SqlSessionStorage({ client, adapter: 'postgres', table: 'omp.sessions' });
// after
new SqlSessionStorage({ client, adapter: 'postgres', table: 'omp_sessions' });
Defensive patterns

Strategy: validation

Validate before calling

const IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/; // mirror of the library check
if (!IDENT_RE.test(tableName)) {
  throw new Error(`table name must be a plain identifier, got: ${tableName}`);
}

Type guard

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

Try / catch

try {
  storage = new SqlSessionStorage({ client, adapter, table });
} catch (err) {
  if (String(err.message).includes('table name must match')) {
    table = tableName.replace(/[^A-Za-z0-9_]/g, '_');
    storage = new SqlSessionStorage({ client, adapter, table });
  } else throw err;
}

Prevention

When it happens

Trigger: new SqlSessionStorage({ ... , table: '<name>' }) where the name fails IDENT_RE — e.g. contains '-', '.', spaces, quotes, or is empty (sql-session-storage.ts:276-279).

Common situations: Deriving the table name from a filename, environment variable, or tenant ID that contains hyphens or dots ('my-sessions', 'omp.sessions'); copy-pasting a qualified name like 'public.sessions'; an empty-string config value.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/596c9802e7a74859. Report an issue: GitHub.