{"record":{"id":"6ca581a10a14fc15","repo":"tursodatabase/turso","slug":"no-active-remote-transaction","errorCode":null,"errorMessage":"No active remote transaction","messagePattern":"No active remote transaction","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"bindings/javascript/sync/packages/common/remote-writer.ts","lineNumber":90,"sourceCode":"            await session.close();\n        }\n    }\n\n    /**\n     * Begin a remote transaction. Creates a session and sends BEGIN.\n     */\n    async beginTransaction(mode: string): Promise<void> {\n        this.session = await this.createSession();\n        await this.session.sequence(\"BEGIN \" + mode);\n        this._inRemoteTxn = true;\n    }\n\n    /**\n     * Commit the remote transaction. Sends COMMIT and closes the session.\n     */\n    async commitTransaction(): Promise<void> {\n        if (!this.session) {\n            throw new Error(\"No active remote transaction\");\n        }\n        try {\n            await this.session.sequence(\"COMMIT\");\n        } finally {\n            this._inRemoteTxn = false;\n            await this.session.close();\n            this.session = null;\n        }\n    }\n\n    /**\n     * Rollback the remote transaction. Sends ROLLBACK and closes the session.\n     */\n    async rollbackTransaction(): Promise<void> {\n        if (!this.session) {\n            throw new Error(\"No active remote transaction\");\n        }\n        try {","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/bindings/javascript/sync/packages/common/remote-writer.ts#L72-L108","documentation":"RemoteWriter.commitTransaction() requires an open session created by beginTransaction(); the session field doubles as the in-remote-transaction flag. If commit is attempted with no session — no BEGIN sent, or a previous COMMIT/ROLLBACK already closed and nulled the session — it throws immediately without touching the network. The finally block guarantees the session is always cleared after the first commit/rollback, which is why double-commit hits this path.","triggerScenarios":"Calling writer.commitTransaction() before any beginTransaction(); calling commit twice (the second has session === null); beginning failed midway so the session was never assigned; calling rollback after commit.","commonSituations":"Transaction wrappers that always issue COMMIT in a finally block even when BEGIN threw; retry logic that replays COMMIT; parallel flows both trying to end the same remote transaction; error paths where BEGIN failed on the server.","solutions":["Only commit after a successful beginTransaction(); check writer.isInTransaction first.","In finally blocks, guard the commit: if (!writer.isInTransaction) skip it.","Make BEGIN failures abort before the callback runs, so COMMIT is never attempted.","Serialize access so only one code path ends a given remote transaction."],"exampleFix":"// before\nawait writer.beginTransaction('DEFERRED');\ntry { await doWork(); } finally { await writer.commitTransaction(); } // BEGIN failure -> commit throws too\n\n// after\nawait writer.beginTransaction('DEFERRED');\nlet ok = false;\ntry { await doWork(); ok = true; }\nfinally {\n  if (ok) await writer.commitTransaction();\n  else await writer.rollbackTransaction().catch(() => {});\n}","handlingStrategy":"validation","validationCode":"if (!writer.isInTransaction) {\n  throw new Error('cannot commit: BEGIN never sent or session already closed');\n}\nawait writer.commitTransaction();","typeGuard":null,"tryCatchPattern":"try { await writer.commitTransaction(); } catch (e) { if (e instanceof Error && e.message === 'No active remote transaction') { /* already ended; treat as no-op */ } else throw e; }","preventionTips":["Commit exactly once per successful beginTransaction().","Never put commitTransaction() in a finally that also runs when BEGIN failed.","Model remote transaction state explicitly in your wrapper instead of inferring it."],"tags":["sync","remote-writer","transaction","javascript"],"backgroundTag":"no-active-transaction","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}