apache/iceberg · warning

Suppressing failure in finally block

Error message

Suppressing failure in finally block

What it means

ExceptionUtil.runSafely() runs a finallyBlock after the guarded operation; if the finally block throws while a primary failure already exists, this warning is logged and the finally exception is attached as suppressed to the primary failure. If no primary failure exists, the finally exception is instead rethrown as e1Class/e2Class/e3Class or RuntimeException. The suppression prevents the finally/cleanup error from masking the original root cause.

Source

Thrown at api/src/main/java/org/apache/iceberg/util/ExceptionUtil.java:125

        } catch (Exception e) {
          LOG.warn("Suppressing failure in catch block", e);
          failure.addSuppressed(e);
        }
      }

      tryThrowAs(failure, e1Class);
      tryThrowAs(failure, e2Class);
      tryThrowAs(failure, e3Class);
      tryThrowAs(failure, RuntimeException.class);
      throw new RuntimeException("Unknown throwable", failure);

    } finally {
      if (finallyBlock != null) {
        try {
          finallyBlock.run();
        } catch (Exception e) {
          if (failure != null) {
            LOG.warn("Suppressing failure in finally block", e);
            failure.addSuppressed(e);
          } else {
            tryThrowAs(e, e1Class);
            tryThrowAs(e, e2Class);
            tryThrowAs(e, e3Class);
            tryThrowAs(e, RuntimeException.class);
            throw new RuntimeException("Unknown exception in finally block", e);
          }
        }
      }
    }
  }

  private static <E extends Exception> void tryThrowAs(Throwable failure, Class<E> excClass)
      throws E {
    if (excClass.isInstance(failure)) {
      throw excClass.cast(failure);
    }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Fix the primary failure reported alongside this warning — it is the root cause; the finally error is secondary.
  2. Inspect the suppressed exception in the logs to see why the finally block failed and harden that cleanup code.
  3. Make finallyBlock idempotent and failure-tolerant (guard close/release calls with try-catch or null/state checks).
  4. If the finally exception is the ONLY failure, it will be rethrown — fix that operation directly since it's then the real error.

Example fix

// before
ExceptionUtil.runSafely(...).finallyBlock(() -> {
  writer.close(); // throws if writer is in broken state
});

// after
ExceptionUtil.runSafely(...).finallyBlock(() -> {
  try {
    writer.close();
  } catch (Exception e) {
    LOG.warn("Failed to close writer in finally", e);
  }
});
Defensive patterns

Strategy: try-catch

Try / catch

// Finally-block failures are suppressed when a primary failure exists; inspect suppressed exceptions.
try {
  operation.run();
} catch (Exception e) {
  LOG.error("Primary failure: {}", e.getMessage(), e);
  for (Throwable s : e.getSuppressed()) {
    LOG.error("Suppressed (finally block): {}", s.getMessage(), s);
  }
  throw e;
}

Prevention

When it happens

Trigger: The finallyBlock.run() supplied to ExceptionUtil.runSafely(...) threw while the guarded body had already failed (failure != null) — e.g. cleanup (close, commit telemetry, release locks) executed during an error path itself threw an exception.

Common situations: Closing writers or releasing resources in a finally block after the main operation already failed, where the close also fails (broken connection, missing file); cleanup code that assumes the happy path succeeded.

Related errors


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