{"record":{"id":"711723f423c68125","repo":"apache/iceberg","slug":"table-already-exists-s-711723","errorCode":null,"errorMessage":"Table already exists: %s","messagePattern":"Table already exists: (.+?)","errorType":"exception","errorClass":"AlreadyExistsException","httpStatus":409,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/CachingCatalog.java","lineNumber":275,"sourceCode":"    @Override\n    public TableBuilder withProperty(String key, String value) {\n      innerBuilder.withProperty(key, value);\n      return this;\n    }\n\n    @Override\n    public Table create() {\n      AtomicBoolean created = new AtomicBoolean(false);\n      Table table =\n          tableCache.get(\n              canonicalizeIdentifier(ident),\n              identifier -> {\n                created.set(true);\n                return innerBuilder.create();\n              });\n\n      if (!created.get()) {\n        throw new AlreadyExistsException(\"Table already exists: %s\", ident);\n      }\n\n      return table;\n    }\n\n    @Override\n    public Transaction createTransaction() {\n      // create a new transaction without altering the cache. the table doesn't exist until the\n      // transaction is\n      // committed. if the table is created before the transaction commits, any cached version is\n      // correct and the\n      // transaction create will fail. if the transaction commits before another create, then the\n      // cache will be empty.\n      return innerBuilder.createTransaction();\n    }\n\n    @Override\n    public Transaction replaceTransaction() {","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/CachingCatalog.java#L257-L293","documentation":"CachingCatalog.create() uses an atomic compute-if-absent over the cache to build the table; if the table was already created by another thread/committer between the caller's request and creation (created flag stays false), the catalog throws AlreadyExistsException. This is the standard 409-style 'already exists' outcome for concurrent creation.","triggerScenarios":"Calling catalog.createTable(ident, ...) or the table builder's create() concurrently from two jobs/threads with the same identifier; a retrying job re-attempting creation after a prior partial success.","commonSituations":"Two Spark/Flink jobs creating the same table at startup; idempotency retries racing with the first successful create; multi-cluster writers against a shared catalog.","solutions":["Treat AlreadyExistsException as success in idempotent create workflows","Fall back to catalog.loadTable(ident) when create fails with already-exists","Coordinate table creation in a single setup step/job before writers start","Use createOrReplaceTransaction if replacement is intended"],"exampleFix":"// before\nTable t = catalog.createTable(ident, schema);\n// after\nTable t;\ntry {\n  t = catalog.createTable(ident, schema);\n} catch (AlreadyExistsException e) {\n  t = catalog.loadTable(ident);\n}","handlingStrategy":"try-catch","validationCode":"if (catalog.tableExists(ident)) { return catalog.loadTable(ident); } // still racy; keep the catch","typeGuard":null,"tryCatchPattern":"catch (AlreadyExistsException e) { return catalog.loadTable(ident); }","preventionTips":["Never assume createTable is concurrency-safe — always handle AlreadyExistsException","Gate table creation behind a single orchestrating job","Prefer load-after-create fallback patterns in shared catalogs"],"tags":["concurrency","catalog","already-exists"],"backgroundTag":"file-already-exists","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}