{"record":{"id":"fe6b057ee3aa6afb","repo":"prestodb/presto","slug":"multi-catalog-write-conflict","errorCode":"MULTI_CATALOG_WRITE_CONFLICT","errorMessage":"Multi-catalog writes not supported in a single transaction. Attempt write to catalog %s, but already wrote to catalog %s","messagePattern":"Multi-catalog writes not supported in a single transaction\\. Attempt write to catalog (.+?), but already wrote to catalog (.+?)","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"error","filePath":"presto-main-base/src/main/java/com/facebook/presto/transaction/InMemoryTransactionManager.java","lineNumber":589,"sourceCode":"        {\n            if (connector instanceof InternalConnector) {\n                return ((InternalConnector) connector).beginTransaction(transactionId, isolationLevel, readOnly);\n            }\n            else {\n                return connector.beginTransaction(isolationLevel, autoCommitContext, readOnly);\n            }\n        }\n\n        public synchronized void checkConnectorWrite(ConnectorId connectorId)\n        {\n            checkOpenTransaction();\n            ConnectorTransactionMetadata transactionMetadata = connectorIdToMetadata.get(connectorId);\n            checkArgument(transactionMetadata != null, \"Cannot record write for connector not part of transaction\");\n            if (readOnly) {\n                throw new PrestoException(READ_ONLY_VIOLATION, \"Cannot execute write in a read-only transaction\");\n            }\n            if (!writtenConnectorId.compareAndSet(null, connectorId) && !writtenConnectorId.get().equals(connectorId)) {\n                throw new PrestoException(\n                        MULTI_CATALOG_WRITE_CONFLICT,\n                        format(\n                                \"Multi-catalog writes not supported in a single transaction. Attempt write to catalog %s, but already wrote to catalog %s\",\n                                connectorId,\n                                writtenConnectorId.get()));\n            }\n            if (transactionMetadata.isSingleStatementWritesOnly() && !autoCommitContext) {\n                throw new PrestoException(AUTOCOMMIT_WRITE_CONFLICT, \"Catalog \" + connectorId + \" only supports writes using autocommit\");\n            }\n        }\n\n        public synchronized ListenableFuture<?> asyncCommit()\n        {\n            if (!completedSuccessfully.compareAndSet(null, true)) {\n                if (completedSuccessfully.get()) {\n                    // Already done\n                    return immediateFuture(null);\n                }","sourceCodeStart":571,"sourceCodeEnd":607,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main-base/src/main/java/com/facebook/presto/transaction/InMemoryTransactionManager.java#L571-L607","documentation":"Thrown by TransactionMetadata.checkConnectorWrite when a single transaction attempts to write through two different connectors. Presto does not support multi-catalog (cross-connector) writes in one transaction: writtenConnectorId uses compareAndSet to record the first writing catalog, and any subsequent write to a different catalog fails.","triggerScenarios":"Two statements in the same transaction write to catalogs with different ConnectorIds, so the second checkConnectorWrite finds writtenConnectorId already set to another catalog and the CAS fails.","commonSituations":"Query writing to two different catalogs (e.g. INSERT into hive then INSERT into mysql) inside one explicit transaction; a federated query with side-effects on multiple connectors; plugin-generated writes to a second catalog.","solutions":["Split the work into separate transactions, one per catalog.","Restrict the transaction's writes to a single catalog.","Use non-transactional (autocommit) execution for the extra-catalog writes.","Redesign the pipeline (e.g. stage data externally) since Presto cannot atomically commit across catalogs."],"exampleFix":"// before\nSTART TRANSACTION;\nINSERT INTO hive.t SELECT * FROM src;\nINSERT INTO mysql.t SELECT * FROM src;\nCOMMIT;\n\n// after: separate transactions per catalog\nSTART TRANSACTION; INSERT INTO hive.t SELECT * FROM src; COMMIT;\nSTART TRANSACTION; INSERT INTO mysql.t SELECT * FROM src; COMMIT;","handlingStrategy":"validation","validationCode":"// Track which catalogs already received writes in this transaction\nSet<ConnectorId> written = new HashSet<>();\nwritten.add(firstConnectorId);\nif (!written.contains(targetConnectorId)) {\n    // would trigger MULTI_CATALOG_WRITE_CONFLICT — commit first, start new transaction\n}","typeGuard":"boolean isSameCatalog(ConnectorId writtenConnectorId, ConnectorId next) {\n    return writtenConnectorId == null || writtenConnectorId.equals(next);\n}","tryCatchPattern":"try {\n    transactionMetadata.checkConnectorWrite(connectorId);\n} catch (PrestoException e) {\n    if (e.getErrorCode().getCode() == StandardErrorCode.MULTI_CATALOG_WRITE_CONFLICT.toErrorCode().getCode()) {\n        // split: finish current transaction, open a new one for this catalog\n        transactionManager.asyncCommit(txId);\n        txId = transactionManager.beginTransaction(false);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Design workloads so each transaction writes to exactly one catalog.","Split cross-catalog pipelines into sequential transactions.","Document which connectors your transaction touches before writing.","Prefer autocommit for the second catalog's writes."],"tags":["transaction","presto","multi-catalog"],"backgroundTag":"multi-catalog-write-conflict","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}