apache/beam · error · IOException

GET request for %s returned null

Error message

GET request for %s returned null

What it means

FhirIoReader.fetchResource calls the Healthcare API client readFhirResource(resourceName) and, because the API can occasionally return a null body even on a successful HTTP call, it converts that null into an explicit IOException naming the resource. This makes the silent-null case observable so the DoFn fails the element and metrics/success counters are not incorrectly incremented.

Source

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

                "Error fetching Fhir resource with ID {} writing to Dead Letter Queue. ",
                resourceId,
                e);
            context.output(FhirIO.Read.DEAD_LETTER, HealthcareIOError.of(resourceId, e));
          }
          if (resource != null) {
            context.output(resource);
          }
        }

        private String fetchResource(HealthcareApiClient client, String resourceName)
            throws IOException, IllegalArgumentException {
          long startTime = Instant.now().toEpochMilli();

          HttpBody resource = client.readFhirResource(resourceName);
          READ_RESOURCE_LATENCY_MS.update(Instant.now().toEpochMilli() - startTime);

          if (resource == null) {
            throw new IOException(String.format("GET request for %s returned null", resourceName));
          }
          READ_RESOURCE_SUCCESS.inc();
          return mapper.writeValueAsString(resource);
        }
      }
    }
  }

  /** The type Write. */
  @AutoValue
  public abstract static class Write extends PTransform<PCollection<String>, Write.AbstractResult> {

    /** The tag for successful writes to FHIR store. */
    public static final TupleTag<String> SUCCESSFUL_BODY = new TupleTag<String>() {};

    /** The tag for the failed writes to FHIR store. */
    public static final TupleTag<HealthcareIOError<String>> FAILED_BODY =
        new TupleTag<HealthcareIOError<String>>() {};

View on GitHub (pinned to 12126d8942)

Solutions

  1. Re-read the resource with a retry (the surrounding pipeline will retry IOExceptions if configured with a failed-inserts/dead-letter sink)
  2. Verify the resourceName points to an existing resource in the correct fhirStore (print/log it and curl the REST endpoint)
  3. Check for deletion races upstream and emit the resource name to a dead-letter output instead of hard-failing
  4. Confirm Cloud Healthcare API quotas/health and regional endpoint correctness

Example fix

// before
HttpBody resource = client.readFhirResource(resourceName);
return mapper.writeValueAsString(resource); // NPE risk
// after
HttpBody resource = client.readFhirResource(resourceName);
if (resource == null) {
  throw new IOException(String.format("GET request for %s returned null", resourceName));
}
return mapper.writeValueAsString(resource);
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight existence check via Healthcare REST API before batch reads
// GET https://healthcare.googleapis.com/v1/{resourceName} and verify 200 + non-empty body

Type guard

boolean isUsableResource(HttpBody body) { return body != null && body.getData() != null; }

Try / catch

try {
  String json = reader.fetchResource(resourceName);
} catch (IOException e) {
  if (e.getMessage().contains("returned null")) {
    // re-fetch with backoff or route resourceName to dead-letter output
  } else throw e;
}

Prevention

When it happens

Trigger: GET on a FHIR store resource where the Cloud Healthcare API returns null — the resource was deleted between listing and reading, the resourceName is malformed, or a transient API problem yielded an empty response body.

Common situations: Race between pipeline listing FHIR resources and their deletion (store re-import/replace); wrong FHIR store path or resource ID casing; intermittent Google API instability where the client swallows the error and returns null.

Understand the failure class

Background: "empty response", "returned no data", "empty embeddings": what HTTP 200-with-empty-body errors mean across libraries — this error's family across 36 libraries.

Related errors


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