apache/beam · error · NoSuchElementException

Error listing FHIR resources from

Error message

Error listing FHIR resources from %s: %s

What it means

The FHIR resource listing page iterator in HttpHealthcareApiClient throws NoSuchElementException when fetching a subsequent page of FHIR search results raises IOException; pageToken is reset to null so iteration ends. The underlying API/network error text is appended after the store name.

Solutions

  1. Retry with backoff — transient network/5xx failures will abort iteration with this error
  2. Re-check IAM (roles/healthcare.fhirStoreViewer) if the embedded cause is 403
  3. Check the trailing %s for the concrete HTTP error (404 store deleted, 429 quota)
  4. Resume iteration from the last successful pageToken if the pipeline supports checkpointing

Example fix

// before
throw new NoSuchElementException(String.format("Error listing FHIR resources from %s: %s", fhirStore, e.getMessage()));
// after
// wrap iteration in retry
FluentBackoff backoff = FluentBackoff.DEFAULT.withMaxRetries(3).withInitialBackoff(Duration.standardSeconds(1));
try {
  return BackOffUtils.next(sleeper, backoff.backoff(), () -> fetchPage(fhirStore));
} catch (IOException | InterruptedException e) {
  throw new RuntimeException("FHIR listing failed for " + fhirStore, e);
}
Defensive patterns

Strategy: retry

Validate before calling

java
if (pageToken != null && pageToken.isEmpty()) {
  throw new IllegalArgumentException("Empty pageToken for FHIR listing");
}

Try / catch

java
try {
  resources = fetchNextPage(fhirStore);
} catch (NoSuchElementException e) {
  // retry with exponential backoff; surface embedded cause after retries exhausted
  throw new IOException("FHIR pagination failed: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Second and later calls to the FHIR resource page iterator's next()/fetch when the FHIR search REST request fails: transient network errors, expired OAuth token, rate limiting, or the fhirStore becoming unavailable mid-iteration.

Common situations: Long-running Beam FHIR pipelines that lose connectivity mid-read; quota/rate-limit 429 responses on large FHIR stores; token refresh failures; concurrent deletion of the store.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/healthcare/HttpHealthcareApiClient.java:996

                "Failed to list first page of FHIR resources from %s: %s",
                fhirStore, e.getMessage()));
      }
    }

    @Override
    public JsonArray next() throws NoSuchElementException {
      try {
        HttpBody response = executeFhirRequest();
        this.isFirstRequest = false;
        JsonObject jsonResponse =
            JsonParser.parseString(mapper.writeValueAsString(response)).getAsJsonObject();
        JsonArray links = jsonResponse.getAsJsonArray("link");
        this.pageToken = parsePageToken(links);
        JsonArray resources = jsonResponse.getAsJsonArray("entry");
        return resources;
      } catch (IOException e) {
        this.pageToken = null;
        throw new NoSuchElementException(
            String.format("Error listing FHIR resources from %s: %s", fhirStore, e.getMessage()));
      }
    }

    private HttpBody executeFhirRequest() throws IOException {
      switch (fhirMethod) {
        case PATIENT_EVERYTHING:
          return client.getPatientEverything(resourceName, parameters, pageToken);
        case SEARCH:
        default:
          return client.searchFhirResource(fhirStore, resourceType, parameters, pageToken);
      }
    }

    private static String parsePageToken(JsonArray links) throws MalformedURLException {
      for (JsonElement e : links) {
        JsonObject link = e.getAsJsonObject();
        if (link.get("relation").getAsString().equalsIgnoreCase("next")) {

View on GitHub (pinned to 12126d8942)