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

  1. Check `transaction.isActive` before writing, or keep all writes inside the active window
  2. Await all addRequestDeferred/record calls before commit/abort
  3. Create a new transaction for post-commit work instead of reusing the old one
  4. 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

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


AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30). Data as JSON: /api/errors/1e7446c5237bf749. Report an issue: GitHub.