earendil-works/pi · error · TypeError

SQLite transaction callbacks must be synchronous

Error message

SQLite transaction callbacks must be synchronous

What it means

Guard inside the synchronous SQLite transaction wrapper: the callback passed to transaction() returned a thenable (isAsyncResult detected a Promise/async result). The wrapper has already executed BEGIN IMMEDIATE and cannot hold the write transaction across an await, so it aborts the COMMIT path and rolls back rather than corrupting transactional state.

Solutions

  1. Make the transaction callback synchronous; async callbacks are not supported inside SQLite transactions.
Defensive patterns

Strategy: type-guard

When it happens

Trigger: Thrown at packages/session-backends/sqlite-node/src/index.ts:82 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of earendil-works/pi@4af9d21d3b (2026-08-24). Data as JSON: /api/errors/a800361a8ab20076. Report an issue: GitHub.

Appendix: source

Thrown at packages/session-backends/sqlite-node/src/index.ts:82

	constructor(db: DatabaseSync) {
		this.db = db;
	}

	exec(sql: string): void {
		this.db.exec(sql);
	}

	prepare(sql: string): SqliteStatement {
		return new NodeSqliteStatement(this.db.prepare(sql));
	}

	transaction<T>(fn: () => T): T {
		sql`BEGIN IMMEDIATE`.exec(this);
		try {
			const result = fn();
			if (isAsyncResult(result)) {
				throw new TypeError("SQLite transaction callbacks must be synchronous");
			}
			sql`COMMIT`.exec(this);
			return result;
		} catch (error) {
			try {
				sql`ROLLBACK`.exec(this);
			} catch {
				// Ignore rollback errors to rethrow original error.
			}
			throw error;
		}
	}

	close(): void {
		this.db.close();
	}
}

View on GitHub (pinned to 4af9d21d3b)