apache/iceberg · error · AlreadyExistsException

View was created concurrently: %s

Error message

View was created concurrently: %s

What it means

When creating a view, the builder commits from a null base metadata. If the metastore's optimistic concurrency check raises CommitFailedException (another process created the view in the meantime), the library converts it to AlreadyExistsException('View was created concurrently') since the null-base commit only succeeds for a fresh view.

Source

Thrown at core/src/main/java/org/apache/iceberg/view/BaseMetastoreViewCatalog.java:216

              .defaultNamespace(defaultNamespace)
              .defaultCatalog(defaultCatalog)
              .timestampMillis(System.currentTimeMillis())
              .putAllSummary(EnvironmentContext.get())
              .build();

      properties.putAll(viewOverrideProperties());

      ViewMetadata viewMetadata =
          ViewMetadata.builder()
              .setProperties(properties)
              .setLocation(null != location ? location : defaultWarehouseLocation(identifier))
              .setCurrentVersion(viewVersion, schema)
              .build();

      try {
        ops.commit(null, viewMetadata);
      } catch (CommitFailedException ignored) {
        throw new AlreadyExistsException("View was created concurrently: %s", identifier);
      }

      return new BaseView(ops, ViewUtil.fullViewName(name(), identifier));
    }

    private View replace(ViewOperations ops) {
      if (tableExists(identifier)) {
        throw new AlreadyExistsException("Table with same name already exists: %s", identifier);
      }

      if (null == ops.current()) {
        throw new NoSuchViewException("View does not exist: %s", identifier);
      }

      Preconditions.checkState(
          !representations.isEmpty(), "Cannot replace view without specifying a query");
      Preconditions.checkState(null != schema, "Cannot replace view without specifying schema");
      Preconditions.checkState(

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Catch AlreadyExistsException and treat the existing view as the outcome (idempotent create)
  2. Retry loadView/createOrReplace after the conflict
  3. Use external locking or a single provisioning job to avoid concurrent DDL

Example fix

// before
view = catalog.buildView(ident).withQuery("sql", q).create();
// after
try {
  view = catalog.buildView(ident).withQuery("sql", q).create();
} catch (AlreadyExistsException e) {
  view = catalog.loadView(ident); // created concurrently
}
Defensive patterns

Strategy: retry

Try / catch

try { return builder.create(); } catch (AlreadyExistsException e) { return catalog.loadView(identifier); /* concurrent creation won */ }

Prevention

When it happens

Trigger: Two concurrent create() calls (or create racing another writer) for the same view identifier; the loser's ops.commit(null, metadata) fails with CommitFailedException.

Common situations: Multiple schedulers/workers running the same DDL job simultaneously; CI pipelines racing to provision views.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/1842e9daa3217d8f. Report an issue: GitHub.