apache/beam · error · IllegalStateException
Error handler is already closed, and may not be closed twice
Error message
Error handler is already closed, and may not be closed twice
What it means
ErrorHandler.close() throws IllegalStateException when called on an already-closed handler. Closing flattens registered error collections into the sink output once; a second close would re-finalize state, so Beam treats double-close as a programming error rather than idempotently succeeding.
Source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/errorhandling/ErrorHandler.java:145
@Override
public boolean isClosed() {
return closed;
}
@Override
public @Nullable OutputT getOutput() {
if (!this.isClosed()) {
throw new IllegalStateException(
"ErrorHandler must be finalized before the output can be returned");
}
return sinkOutput;
}
@Override
public void close() {
if (closed) {
throw new IllegalStateException(
"Error handler is already closed, and may not be closed twice");
}
closed = true;
PCollection<ErrorT> flattened;
if (errorCollections.isEmpty()) {
LOG.info("Empty list of error pcollections passed to ErrorHandler.");
flattened = pipeline.apply(Create.empty(coder));
} else {
flattened = PCollectionList.of(errorCollections).apply(Flatten.pCollections());
}
LOG.debug(
"{} error collections are being sent to {}",
errorCollections.size(),
sinkTransform.getName());
String sinkTransformName = sinkTransform.getName();
sinkOutput =
flattened
.apply(View on GitHub (pinned to 12126d8942)
Solutions
- Close the handler exactly once — remove duplicate close() calls.
- Prefer try-with-resources and drop manual close() calls.
- Track closed state in application code if multiple code paths may finalize the handler.
Example fix
// before
try (ErrorHandler<BadRecord> h = errorHandler) {
...
errorHandler.close(); // double close
}
// after
try (ErrorHandler<BadRecord> h = errorHandler) {
... // close happens automatically once
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!closedOnce) { errorHandler.close(); closedOnce = true; } Type guard
class CloseOnce<T> implements AutoCloseable {
private final ErrorHandler<T> delegate; private boolean closed = false;
CloseOnce(ErrorHandler<T> d){this.delegate=d;}
public void close(){ if(!closed){ delegate.close(); closed=true; } }
} Try / catch
try {
errorHandler.close();
} catch (IllegalStateException e) {
LOG.warn("Handler already closed; ignoring", e);
} Prevention
- Use try-with-resources exclusively; never also call close() manually
- Track closure with a boolean wrapper if multiple paths may close
- Audit finalize/cleanup code paths for duplicate close calls
When it happens
Trigger: Calling close() twice on the same DefaultErrorHandler — commonly via try-with-resources plus a manual close() call, or a __exit__ path (Python interop / with-statement semantics) that closes the handler after user code already closed it.
Common situations: Combining with-statement usage with explicit close(); closing the handler in both an expand() helper and the caller; re-running a finalize method during retries of pipeline construction.
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
- Error collections cannot be added after Error Handler is clo
- ErrorHandler must be finalized before the output can be retu
- Encountered Bad Record:
- No Op handler should not be closed. This implies this IO is
- No Op handler has no output. This implies this IO is misconf
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/207a106ef14c87fc.
Report an issue: GitHub.