apache/beam · error
response contains bad status
Error message
response contains bad status: [%v]
What it means
fhirio's extractBodyFrom validates the HTTP response from the FHIR store API with googleapi.CheckResponse before reading the body. When the response carries a non-2xx status (auth failure, not found, malformed request, server error), CheckResponse returns an error and this error wraps it together with the response's status line, so the caller sees both the HTTP status and the underlying API error details.
Solutions
- Check the wrapped googleapi error's code and body for the server-side detail (401/403 → fix credentials/IAM; 404 → fix store path; 400 → fix query)
- Verify the pipeline's service account has the required healthcare.fhirStore permissions (viewer/searcher roles)
- Validate the FHIR store resource path and search parameters against the API docs before retrying
- Wrap the call in retry with backoff for transient 5xx statuses
Example fix
// before
resp, err := client.Search(ctx, store, "Patient", badParams) // 400
// after
params := url.Values{}
params.Set("family", "Smith") // valid FHIR search parameter
resp, err := client.Search(ctx, validStorePath, "Patient", params)
if err != nil {
var gerr *googleapi.Error
if errors.As(err, &gerr) { log.Printf("status %d: %s", gerr.Code, gerr.Body) }
} Defensive patterns
Strategy: try-catch
Validate before calling
func validateFHIRStorePath(storePath string) error {
re := regexp.MustCompile(`^projects/[^/]+/locations/[^/]+/datasets/[^/]+/fhirStores/[^/]+$`)
if !re.MatchString(storePath) {
return fmt.Errorf("invalid FHIR store path: %s", storePath)
}
return nil
} Try / catch
result, err := fhirio.Search(s, store, "Patient").Get(...)
if err != nil {
var gerr *googleapi.Error
if errors.As(err, &gerr) {
switch gerr.Code {
case 401, 403:
log.Printf("auth/IAM issue: %v", gerr)
case 404:
log.Printf("store not found: %v", gerr)
case 400:
log.Printf("bad query: %v", gerr)
default:
// transient 5xx: retry with backoff
}
}
} Prevention
- Grant the pipeline service account the correct healthcare.fhirStore roles
- Validate the FHIR store resource path before deploying
- Test search parameters against the FHIR API before running the pipeline
- Implement retry-with-backoff for transient 5xx responses
When it happens
Trigger: Any fhirio transformation (search, read, executeBundle, searchResourcesPaginated) hitting a FHIR store that returns an HTTP error status: invalid FHIR store path, insufficient IAM permissions, bad query parameters (e.g. invalid search filter), quota exceeded, or transient 5xx from the service.
Common situations: Misconfigured FHIR store URL or dataset/location path; missing roles/healthcare.fhirStore* IAM bindings on the pipeline's service account; expired or missing OAuth credentials; a search query with unsupported or misspelled FHIR search parameters yielding 400; transient 500/503 during high load.
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
- Failed to import with error. Moving to deadletter path
- Failed to initialize Google Cloud Healthcare Service…
- Failed to retrieve secret bytes for secret
- 2xx codes should not be exceptions. Got status code
- A VPC network must be provided to use a private endpoint.
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/fda3ada697913341.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/io/fhirio/common.go:59
operationErrorCounterName = baseMetricPrefix + "operation_error_count"
operationSuccessCounterName = baseMetricPrefix + "operation_success_count"
successCounterName = baseMetricPrefix + "resource_success_count"
pageTokenParameterKey = "_page_token"
)
var backoffDuration = [...]time.Duration{time.Second, 5 * time.Second, 10 * time.Second, 15 * time.Second}
func executeAndRecordLatency[T any](ctx context.Context, latencyMs *beam.Distribution, executionSupplier func() (T, error)) (T, error) {
timeBeforeReadRequest := time.Now()
result, err := executionSupplier()
latencyMs.Update(ctx, time.Since(timeBeforeReadRequest).Milliseconds())
return result, err
}
func extractBodyFrom(response *http.Response) (string, error) {
err := googleapi.CheckResponse(response)
if err != nil {
return "", errors.Wrapf(err, "response contains bad status: [%v]", response.Status)
}
bodyBytes, err := io.ReadAll(response.Body)
if err != nil {
return "", err
}
return string(bodyBytes), nil
}
type operationCounters struct {
successCount, errorCount beam.Counter
}
func (c *operationCounters) setup(namespace string) {
c.successCount = beam.NewCounter(namespace, operationSuccessCounterName)
c.errorCount = beam.NewCounter(namespace, operationErrorCounterName)
}View on GitHub (pinned to 12126d8942)