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
- Re-read the resource with a retry (the surrounding pipeline will retry IOExceptions if configured with a failed-inserts/dead-letter sink)
- Verify the resourceName points to an existing resource in the correct fhirStore (print/log it and curl the REST endpoint)
- Check for deletion races upstream and emit the resource name to a dead-letter output instead of hard-failing
- 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
- Emit failed resource names to a dead-letter PCollection instead of letting them fail the bundle silently
- Check that resources are not being deleted concurrently with the pipeline
- Monitor Cloud Healthcare API error rates/quotas
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
- The PCollection tuple must have the FhirIO.Read.OUT and Fhir
- The PCollection tuple bodies must have the FhirIO.Write.SUCC
- Not all temporary files are present for importing.
- Export cannot be executed because export URI (%s) is not fro
- Export operation (%s) failed. Reason: %s
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/039c9ef69ccd4ceb.
Report an issue: GitHub.