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
- Fix the primary failure reported alongside this warning — it is the root cause; the finally error is secondary.
- Inspect the suppressed exception in the logs to see why the finally block failed and harden that cleanup code.
- Make finallyBlock idempotent and failure-tolerant (guard close/release calls with try-catch or null/state checks).
- 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
- Make finallyBlock cleanup idempotent and guarded with internal try-catch.
- Never let close()/release() in finally assume the happy path succeeded.
- Log getSuppressed() to surface hidden finally failures.
- If no primary failure exists, the finally exception is rethrown — ensure that operation is itself robust.
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
- Suppressing failure in catch block
- Suppressing exception in catch: {}
- Unknown exception in finally block
- The iceberg transaction has been committed, but we failed to
- Suppressing exception in catch: {}
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/e72c2a9942b1d860.
Report an issue: GitHub.