apache/iceberg · warning · RuntimeException
Unknown throwable
Error message
Unknown throwable
What it means
ExceptionUtil.runSafely's fallback: when the block fails with a Throwable that is not an instance of any of the three declared exception classes nor a RuntimeException, it is wrapped in RuntimeException("Unknown throwable"). This happens with checked exceptions outside the declared set (Errors are re-thrown by tryThrowAs only if matching; arbitrary Throwables land here).
Source
Thrown at api/src/main/java/org/apache/iceberg/util/ExceptionUtil.java:117
return block.run();
} catch (Throwable t) {
failure = t;
if (catchBlock != null) {
try {
catchBlock.run(failure);
} 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);
}
}
}View on GitHub (pinned to 86d9c8fc54)
Solutions
- Unwrap the cause: e.getCause() holds the original throwable
- Add the actual exception class as another runSafely type parameter so it is rethrown directly
- Use the catchBlock callback to handle unexpected exception types before the fallback wraps them
- Broaden the Block's declared exceptions (e.g. add E2) to cover the real failure type
Example fix
// before ExceptionUtil.runSafely(block, null, null, IOException.class); // custom MyException gets wrapped // after ExceptionUtil.runSafely(block, null, null, IOException.class, MyException.class);
Defensive patterns
Strategy: try-catch
Try / catch
try {
ExceptionUtil.runSafely(block, catchBlock, finallyBlock, E1.class, E2.class, E3.class);
} catch (RuntimeException e) {
if ("Unknown throwable".equals(e.getMessage()) && e.getCause() != null) {
handleOriginal(e.getCause());
} else {
throw e;
}
} Prevention
- Declare every checked exception the block can throw in runSafely's type parameters
- Use the catchBlock to absorb or classify unexpected exception types
- Update runSafely parameters whenever a called library adds new checked exceptions
- Always check getCause() on RuntimeExceptions from runSafely
When it happens
Trigger: The Block throws a checked exception type not among e1Class/e2Class/e3Class (e.g. a Block declaring IOException but throwing a custom checked exception), and no catchBlock absorbs it.
Common situations: Refactoring that adds a new exception type to a lambda without updating runSafely's declared exception classes; library upgrades introducing new checked exceptions from called code.
Related errors
- Unknown exception in finally block
- Can't retrieve values from an empty struct
- Can't modify an empty struct
- %s doesn't implement cleanupLevel
- %s doesn't implement cleanExpiredMetadata
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/d9da1b75054d9301.
Report an issue: GitHub.