{"record":{"id":"1e7446c5237bf749","repo":"apify/crawlee","slug":"cannot-record-a-journal-entry-on-a-transaction-in","errorCode":null,"errorMessage":"Cannot record a journal entry on a transaction in the '${this.#state}' state","messagePattern":"Cannot record a journal entry on a transaction in the '(.+?)' state","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/storages/transaction.ts","lineNumber":207,"sourceCode":"     * `true` only while `state === 'open'`. This is the single predicate every storage operation\n     * consults — operations performed after the transaction is closed pass through to the real backend.\n     */\n    get isActive(): boolean {\n        return this.#state === 'open';\n    }\n\n    /** Runs `callback` with this transaction installed in the async context. */\n    async run<T>(callback: () => Awaitable<T>): Promise<T> {\n        return transactionStorage.run(this, async () => callback());\n    }\n\n    /**\n     * Records a write operation in the journal.\n     * @internal\n     */\n    recordJournalEntry(entry: JournalEntry): void {\n        if (!this.isActive) {\n            throw new Error(`Cannot record a journal entry on a transaction in the '${this.#state}' state`);\n        }\n\n        this.journal.push(entry);\n    }\n\n    /**\n     * Replays the journaled writes into real storage. A no-op unless the transaction is `open`.\n     *\n     * The transaction transitions to `committing` *before* anything is flushed, so a commit that throws\n     * partway lands in `failed` (never back in `open`) and subsequent storage operations pass through\n     * rather than recording into a dead transaction. Delivery is at-least-once — a commit that fails\n     * partway may have applied some of the writes already.\n     */\n    async commit(): Promise<void> {\n        if (this.#state !== 'open') {\n            return;\n        }\n","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/core/src/storages/transaction.ts#L189-L225","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nqueue.addRequestDeferred(url); // resolves later\nawait transaction.commit(); // throws when journal write lands\n// after\nconst deferred = queue.addRequestDeferred(url);\nawait deferred.promise;\nawait transaction.commit();","handlingStrategy":"validation","validationCode":"if (!transaction.isActive) {\n  throw new Error('Refusing to write: transaction is no longer active');\n}","typeGuard":"null","tryCatchPattern":"try {\n  transaction.recordRequestJournalEntry(entry);\n} catch (err) {\n  if (/journal entry on a transaction/.test(String(err))) {\n    logger.warning('Write after transaction end; starting a new transaction');\n    transaction = await beginTransaction();\n  } else throw err;\n}","preventionTips":["Always await addRequestDeferred promises before commit/abort","Check isActive before every journal write","Scope transactions tightly and avoid sharing them across async tasks"],"tags":["transaction","state","storage"],"backgroundTag":"transaction-already-committed","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}