apify/crawlee · error · Error
Cannot record a journal entry on a transaction in the '${thi
Error message
Cannot record a journal entry on a transaction in the '${this.#state}' state What it means
Transaction.recordJournalEntry refuses journal writes on a transaction that is no longer active (already committed, aborted, or not yet started). It is an internal API used by recordRequestJournalEntry and addRequestDeferred, so hitting it means a storage write was attempted after the transaction finished.
Source
Thrown at packages/core/src/storages/transaction.ts:207
* `true` only while `state === 'open'`. This is the single predicate every storage operation
* consults — operations performed after the transaction is closed pass through to the real backend.
*/
get isActive(): boolean {
return this.#state === 'open';
}
/** Runs `callback` with this transaction installed in the async context. */
async run<T>(callback: () => Awaitable<T>): Promise<T> {
return transactionStorage.run(this, async () => callback());
}
/**
* Records a write operation in the journal.
* @internal
*/
recordJournalEntry(entry: JournalEntry): void {
if (!this.isActive) {
throw new Error(`Cannot record a journal entry on a transaction in the '${this.#state}' state`);
}
this.journal.push(entry);
}
/**
* Replays the journaled writes into real storage. A no-op unless the transaction is `open`.
*
* The transaction transitions to `committing` *before* anything is flushed, so a commit that throws
* partway lands in `failed` (never back in `open`) and subsequent storage operations pass through
* rather than recording into a dead transaction. Delivery is at-least-once — a commit that fails
* partway may have applied some of the writes already.
*/
async commit(): Promise<void> {
if (this.#state !== 'open') {
return;
}
View on GitHub (pinned to dbe57fb09c)
Solutions
- Check `transaction.isActive` before writing, or keep all writes inside the active window
- Await all addRequestDeferred/record calls before commit/abort
- Create a new transaction for post-commit work instead of reusing the old one
- Fix handlers that write after abort (guard with try/finally and state checks)
Example fix
// before queue.addRequestDeferred(url); // resolves later await transaction.commit(); // throws when journal write lands // after const deferred = queue.addRequestDeferred(url); await deferred.promise; await transaction.commit();
Defensive patterns
Strategy: validation
Validate before calling
if (!transaction.isActive) {
throw new Error('Refusing to write: transaction is no longer active');
} Type guard
null
Try / catch
try {
transaction.recordRequestJournalEntry(entry);
} catch (err) {
if (/journal entry on a transaction/.test(String(err))) {
logger.warning('Write after transaction end; starting a new transaction');
transaction = await beginTransaction();
} else throw err;
} Prevention
- Always await addRequestDeferred promises before commit/abort
- Check isActive before every journal write
- Scope transactions tightly and avoid sharing them across async tasks
When it happens
Trigger: Adding requests or recording request journal entries after commit() or abort(); sharing a Transaction across async tasks that complete after the transaction closed; calling deferred request adders whose promises resolve post-commit.
Common situations: Un-awaited addRequestDeferred calls settling after commit; retry/error handlers writing to a transaction that was rolled back on error; long-running loops using a stale transaction reference.
Related errors
- ${operation} cannot be used inside a storage transaction: ${
- ${operation} cannot be used inside a storage transaction: ${
- Request ID does not match its uniqueKey.
- StateValidationError(persistStateKey, result.issues)
- Recoverable state has not yet been loaded - call initialize(
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/1e7446c5237bf749.
Report an issue: GitHub.