{"record":{"id":"5dc09115151281fb","repo":"appwrite/appwrite","slug":"transaction-limit-exceeded-5dc091","errorCode":"transaction_limit_exceeded","errorMessage":"Transaction already has {existing} operations, adding 1 would exceed the maximum of {maxBatch}","messagePattern":"Transaction already has (.+?) operations, adding 1 would exceed the maximum of (.+?)","errorType":"error_code","errorClass":"Appwrite\\Extend\\Exception","httpStatus":400,"severity":"error","filePath":"src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php","lineNumber":418,"sourceCode":"                : $dbForProject->getDocument('transactions', $transactionId);\n            if ($transaction->isEmpty()) {\n                throw new Exception(Exception::TRANSACTION_NOT_FOUND, params: [$transactionId]);\n            }\n            if ($transaction->getAttribute('status', '') !== 'pending') {\n                throw new Exception(Exception::TRANSACTION_NOT_READY);\n            }\n\n            $now = new \\DateTime();\n            $expiresAt = new \\DateTime($transaction->getAttribute('expiresAt', 'now'));\n            if ($now > $expiresAt) {\n                throw new Exception(Exception::TRANSACTION_EXPIRED);\n            }\n\n            // Enforce max operations per transaction\n            $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION;\n            $existing = $transaction->getAttribute('operations', 0);\n            if (($existing + 1) > $maxBatch) {\n                throw new Exception(\n                    Exception::TRANSACTION_LIMIT_EXCEEDED,\n                    'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch\n                );\n            }\n\n            $staged = new Document([\n                '$id' => ID::unique(),\n                'databaseInternalId' => $database->getSequence(),\n                'collectionInternalId' => $collection->getSequence(),\n                'transactionInternalId' => $transaction->getSequence(),\n                'documentId' => $isBulk ? null : $documentId,\n                'action' => $isBulk ? 'bulkCreate' : 'create',\n                'data' => $isBulk ? $documents : $documents[0],\n            ]);\n\n            $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) {\n                $dbForProject->createDocument('transactionLogs', $staged);\n                $dbForProject->increaseDocumentAttribute(","sourceCodeStart":400,"sourceCodeEnd":436,"githubUrl":"https://github.com/appwrite/appwrite/blob/feb9831e60c12102a2c75891715c7401d0f9a89c/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php#L400-L436","documentation":"Thrown when adding one more operation would exceed the maximum number of operations allowed in a single Appwrite transaction. The limit comes from the project plan's databasesTransactionSize setting, falling back to the APP_LIMIT_DATABASE_TRANSACTION constant. The current count is read from the transaction's operations attribute, and staging is rejected if existing + 1 exceeds the cap.","triggerScenarios":"POST /v1/databases/{databaseId}/collections/{collectionId}/documents with a transactionId when the transaction already holds exactly its maximum number of staged operations (e.g. plan limit reached by earlier creates/updates/deletes in the same transaction).","commonSituations":"Bulk import scripts staging hundreds of operations into one transaction on a plan with a small databasesTransactionSize; mixing bulk creates (which may count differently) with other staged ops; assuming the default limit applies while the project plan sets a lower one.","solutions":["Commit the current transaction and continue remaining operations in a new transaction (chunk the work)","Read the transaction's operations attribute before staging and stop before hitting the cap","Raise the plan's databasesTransactionSize limit (upgrade) if the workload legitimately needs larger transactions","Reduce operations per transaction by batching document writes where the API supports it"],"exampleFix":"// before\nfor (const doc of docs) {\n  await db.createDocument('db', 'coll', ID.unique(), doc, undefined, undefined, undefined, undefined, txId); // fails at limit\n}\n\n// after\nconst CHUNK = 50;\nfor (let i = 0; i < docs.length; i += CHUNK) {\n  const tx = await dbTransactions.create();\n  for (const doc of docs.slice(i, i + CHUNK)) {\n    await db.createDocument('db', 'coll', ID.unique(), doc, undefined, undefined, undefined, undefined, tx.$id);\n  }\n  await dbTransactions.commit(tx.$id);\n}","handlingStrategy":"validation","validationCode":"const tx = await dbTransactions.get(txId);\nconst MAX = 100; // keep in sync with plan's databasesTransactionSize\nif ((tx.operations ?? 0) + 1 > MAX) {\n  throw new Error(`operation cap reached (${tx.operations}) — commit and open a new transaction`);\n}","typeGuard":"function isTransactionLimitExceeded(e) {\n  return e?.type === 'transaction_limit_exceeded';\n}","tryCatchPattern":"try {\n  await db.createDocument('db', 'coll', ID.unique(), payload, undefined, undefined, undefined, undefined, txId);\n} catch (e) {\n  if (isTransactionLimitExceeded(e)) {\n    await dbTransactions.commit(txId);\n    const tx = await dbTransactions.create();\n    await db.createDocument('db', 'coll', ID.unique(), payload, undefined, undefined, undefined, undefined, tx.$id);\n  } else throw e;\n}","preventionTips":["Know your plan's databasesTransactionSize before designing batches","Track staged-operation counts client-side and chunk below the cap","Make each staged operation idempotent so a re-staged batch is safe","Commit as soon as a logical unit of work completes"],"tags":["databases","documents","transactions","limits","quota"],"backgroundTag":"transaction-size-limit","analyzedSha":"feb9831e60c12102a2c75891715c7401d0f9a89c","analyzedAt":"2026-08-18T21:34:56.994Z","contentChangedAt":"2026-08-18T21:34:56.994Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}