apache/beam · error · IllegalStateException

One or more ErrorHandlers aren't closed, and this pipeline c

Error message

One or more ErrorHandlers aren't closed, and this pipeline cannot be run. See the ErrorHandler documentation for expected usage

What it means

Before running, Pipeline.validateErrorHandlers() verifies that every ErrorHandler registered on the pipeline has been closed. An unclosed handler means error output (e.g. a write sink attached to bad-record routing) was never finalized, so run() throws this IllegalStateException to prevent silently dropping error records.

Source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/Pipeline.java:737

    public String apply(@Nonnull final Map.Entry<String, Collection<PTransform<?, ?>>> input) {
      return input.getKey();
    }
  }

  private static class IsUnique<K, V> implements Predicate<Map.Entry<K, Collection<V>>> {
    @SuppressFBWarnings(
        value = "NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION",
        justification = "https://github.com/google/guava/issues/920")
    @Override
    public boolean apply(@Nonnull final Map.Entry<K, Collection<V>> input) {
      return input != null && input.getValue().size() == 1;
    }
  }

  private void validateErrorHandlers() {
    for (ErrorHandler<?, ?> errorHandler : errorHandlers) {
      if (!errorHandler.isClosed()) {
        throw new IllegalStateException(
            "One or more ErrorHandlers aren't closed, and this pipeline "
                + "cannot be run. See the ErrorHandler documentation for expected usage");
      }
    }
  }
}

View on GitHub (pinned to 12126d8942)

Solutions

  1. Call close() on every registered ErrorHandler after attaching its sink and before pipeline.run().
  2. Use try/finally around pipeline construction so close() runs even if intermediate steps throw.
  3. If the handler is genuinely unused, remove its registration from the pipeline instead of leaving it open.
  4. Review the ErrorHandler Javadoc usage pattern: create, wire into transforms, close, then run.

Example fix

// before
// ErrorHandler<PCollectionTuple, ? > h = pipeline.registerErrorHandler(...); pipeline.run(); // throws
// after
// ErrorHandler<PFileSystem, ? > h = pipeline.registerErrorHandler(...); ... h.close(); pipeline.run();
Defensive patterns

Strategy: try-catch

Validate before calling

// Java: verify all handlers are closed before run
for (ErrorHandler<?, ?> h : registeredHandlers) {
  if (!h.isClosed()) throw new IllegalStateException("Handler not closed: " + h);
}

Try / catch

ErrorHandler<...> h = pipeline.registerErrorHandler(...);
try {
  // wire transforms using h
  h.close();
  pipeline.run();
} catch (IllegalStateException e) {
  if (e.getMessage().contains("ErrorHandlers aren't closed")) {
    h.close();
    pipeline.run(); // retry once closed
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling pipeline.run() while an ErrorHandler obtained via pipeline.registerErrorHandler(...) / construction is still open — i.e. the user never called errorHandler.close() (which attaches the error-collection sink) after wiring transforms.

Common situations: Using error handlers for write failures (e.g. BigQuery/Nio bad-record routing) and forgetting the close() step; early returns or exceptions in pipeline-construction code that skip the close call.

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/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/7a4715bdc7a94aec. Report an issue: GitHub.