apache/beam · error · IOException

DeidentifyFhirStore operation

Error message

DeidentifyFhirStore operation (%s) failed.

What it means

FhirIO's deidentify step polls the DeidentifyFhirStore long-running operation and throws an IOException when the finished operation reports an error, embedding the operation name. Unlike the export path, no reason string is appended here — the developer must look the operation up (e.g. via projects.locations.operations.get) to learn why de-identification failed. The IOException allows pipeline retry/dead-letter handling to engage.

Solutions

  1. Fetch the operation by name (gcloud healthcare operations describe) to see the underlying error detail and fix the reported cause
  2. Validate the DeidentifyConfig ( transformations, dateShiftConfig) fields before running
  3. Grant the Healthcare service agent access to both source and destination FHIR stores
  4. Retry after transient API failures — the thrown IOException participates in Beam retry semantics

Example fix

// before
DeidentifyConfig cfg = DeidentifyConfig.newBuilder().build(); // empty config may be invalid
// after
DeidentifyConfig cfg = DeidentifyConfig.newBuilder()
    .setInfoTypeTransformations(InfoTypeTransformations.newBuilder()
        .addTransformations(InfoTypeTransformation.newBuilder()
            .setRedactConfig(RedactConfig.getDefaultInstance()).build()).build())
    .build();
Defensive patterns

Strategy: try-catch

Validate before calling

// validate the DeidentifyConfig structure before submitting
 Preconditions.checkNotNull(cfg.getInfoTypeTransformations(), "deidentify transformations required");

Try / catch

try {
  // run deidentify
} catch (IOException e) {
  if (e.getMessage().contains("DeidentifyFhirStore operation")) {
    String opName = extractOperationName(e.getMessage());
    // describe operation to get the reason, fix config/IAM, retry
  } else throw e;
}

Prevention

When it happens

Trigger: Polling a DeidentifyFhirStore operation that completed with operation.getError() != null — commonly caused by insufficient IAM permissions on the source/destination FHIR store, an invalid deidentify config (e.g. bad TextConfig/InfoType transformations), or a source/destination store misconfiguration.

Common situations: De-identifying into a destination FHIR store the service account can't create/write to; misconfigured date-shift or crypto-hash config fields; regional mismatch between source and destination stores; quota exhaustion on the Healthcare API.

Related errors


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

Appendix: source

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

        this.client = new HttpHealthcareApiClient();
      }

      @ProcessElement
      public void deidentify(ProcessContext context) throws IOException, InterruptedException {
        String sourceFhirStore = context.element();
        String destinationFhirStore = this.destinationFhirStore.get();
        DeidentifyConfig deidConfig = gson.fromJson(this.deidConfigJson, DeidentifyConfig.class);
        Operation operation =
            client.deidentifyFhirStore(sourceFhirStore, destinationFhirStore, deidConfig);
        operation = client.pollOperation(operation, 15000L);
        incrementLroCounters(
            operation,
            DEIDENTIFY_OPERATION_SUCCESS,
            DEIDENTIFY_OPERATION_ERRORS,
            RESOURCES_DEIDENTIFIED_SUCCESS,
            RESOURCES_DEIDENTIFIED_ERRORS);
        if (operation.getError() != null) {
          throw new IOException(
              String.format("DeidentifyFhirStore operation (%s) failed.", operation.getName()));
        }
        context.output(destinationFhirStore);
      }
    }
  }

  /** The type Search. */
  public static class Search<T>
      extends PTransform<PCollection<FhirSearchParameter<T>>, FhirIO.Search.Result> {

    private final ValueProvider<String> fhirStore;

    Search(ValueProvider<String> fhirStore) {
      this.fhirStore = fhirStore;
    }

    Search(String fhirStore) {

View on GitHub (pinned to 12126d8942)