apache/beam · error · IOException

Error executing batch GCS request

Error message

Error executing batch GCS request

What it means

GcsUtilV1.executeBatches fans GCS operations out across an executor and waits on all futures with MoreFutures.get. If any batch task fails, the ExecutionException is converted into this generic IOException unless the cause is a FileNotFoundException (rethrown as-is). It is an umbrella error; the real per-operation failure is in the cause chain.

Source

Thrown at sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp/util/GcsUtilV1.java:964

                MAX_CONCURRENT_BATCHES,
                MAX_CONCURRENT_BATCHES,
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>()));

    List<CompletionStage<Void>> futures = new ArrayList<>();
    for (final BatchInterface batch : batches) {
      futures.add(MoreFutures.runAsync(batch::execute, executor));
    }

    try {
      try {
        MoreFutures.get(MoreFutures.allOf(futures));
      } catch (ExecutionException e) {
        if (e.getCause() instanceof FileNotFoundException) {
          throw (FileNotFoundException) e.getCause();
        }
        throw new IOException("Error executing batch GCS request", e);
      } finally {
        // Give the other batches a chance to complete in error cases.
        executor.shutdown();
        if (!executor.awaitTermination(5, TimeUnit.MINUTES)) {
          LOG.warn("Taking over 5 minutes to flush gcs op batches after error");
          executor.shutdownNow();
          if (!executor.awaitTermination(5, TimeUnit.MINUTES)) {
            LOG.warn("Took over 10 minutes to flush gcs op batches after error and interruption.");
          }
        }
      }
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new IOException("Interrupted while executing batch GCS request", e);
    }
  }

  /**

View on GitHub (pinned to 12126d8942)

Solutions

  1. Unwrap e.getCause() (and its cause chain) to find the specific failed operation
  2. Check per-object permissions if causes show 403
  3. Retry the batch; individual failed items will surface again
  4. Avoid wrapping operations that produce FileNotFoundException — those are rethrown directly

Example fix

// before
try {
  gcsUtil.removeBatches(batches);
} catch (IOException e) {
  LOG.error("batch failed", e);
}
// after
try {
  gcsUtil.removeBatches(batches);
} catch (IOException e) {
  Throwable cause = e.getCause();
  LOG.error("batch failed: {}", cause == null ? e : cause, e);
  throw e;
}
Defensive patterns

Strategy: try-catch

Type guard

boolean hasFileNotFoundCause(Throwable t) {
  while (t != null) { if (t instanceof FileNotFoundException) return true; t = t.getCause(); }
  return false;
}

Try / catch

try {
  gcsUtil.removeBatches(batches);
} catch (FileNotFoundException e) {
  // per-library semantics: item missing
} catch (IOException e) {
  Throwable root = e; while (root.getCause() != null) root = root.getCause();
  LOG.error("batch op failed: {}", root.getMessage(), e);
  throw e;
}

Prevention

When it happens

Trigger: Any batched GCS operation (e.g. removeBatches, copy batches) failing inside the executor — network errors, permission errors, or API errors on individual sub-requests.

Common situations: Bulk deletes/copies where one object hits a 500 or permission error; transient GCS outages during large batch flushes.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/facb6313f7e27964. Report an issue: GitHub.