apache/beam · warning

Error fetching HL7v2 message with ID

Error message

Error fetching HL7v2 message with ID {} writing to Dead Letter Queue. 

What it means

HL7v2IO's fetchMessage DoFn retrieves each HL7v2 message by ID from the Healthcare API. On any exception during the GET (null response, API error, network failure), failedMessageGets is incremented and this warning logged with the message ID and cause; the exception is then rethrown so the pipeline's failure handling (e.g. HealthcareIOError wrapping via HL7v2IO.Read's error handling) decides the outcome.

Solutions

  1. Check the logged cause per msgId — 404 means the message was deleted/purged; 403 means IAM is missing.
  2. Grant the service account roles/healthcare.hl7V2MessageViewer on the store/dataset.
  3. Wrap the Read with error handling so failures go to a dead letter instead of failing the whole pipeline (HL7v2IO supports HealthcareIOError dead-letter on Read).
  4. Verify the hl7v2Store path format and that Pub/Sub notification IDs are still retained by the store's message retention config.

Example fix

// before
pipeline.apply(HL7v2IO.read().hl7V2Store(store)); // failures abort pipeline
// after
PCollection<HealthcareIOError<String>> dlq =
    pipeline.apply(HL7v2IO.read().hl7V2Store(store)).get(HL7v2IO.Read.DEAD_LETTER);
Defensive patterns

Strategy: try-catch

Validate before calling

// Java: validate store path before HL7v2IO.read()
if (!store.matches("projects/[^/]+/locations/[^/]+/datasets/[^/]+/hl7V2Stores/[^/]+")) {
  throw new IllegalArgumentException("Invalid HL7v2 store: " + store);
}

Try / catch

// Wrap per-message fetch and rethrow only after dead-lettering
try {
  HL7v2Message msg = fetchMessage(msgId);
} catch (java.io.IOException e) {
  failedMessageGets.inc();
  context.output(DEAD_LETTER, HealthcareIOError.of(msgId, e));
  throw e;
}

Prevention

When it happens

Trigger: Reading HL7v2IO.read().hl7V2Store(store) with message IDs that don't exist or were deleted; missing roles/healthcare.hl7V2MessageViewer permission; malformed store path; transient Healthcare API 5xx/timeout.

Common situations: Notifications from Pub/Sub referencing messages already purged; IAM changes mid-run; store name typos; quota exhaustion causing GET failures.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

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

    private final HealthcareApiClient client;

    /** Instantiates a new HL7v2MessageClient (HCLS API v1). */
    HL7v2MessageClient(HealthcareApiClient client) {
      this.client = client;
    }

    private HL7v2Message fetchMessage(String msgId)
        throws IOException, ParseException, IllegalArgumentException {
      try {
        com.google.api.services.healthcare.v1.model.Message msg = client.getHL7v2Message(msgId);
        if (msg == null) {
          throw new IOException(String.format("GET request for %s returned null", msgId));
        }
        this.successfulHL7v2MessageGets.inc();
        return HL7v2Message.fromModel(msg);
      } catch (Exception e) {
        failedMessageGets.inc();
        LOG.warn(
            "Error fetching HL7v2 message with ID {} writing to Dead Letter Queue. ", msgId, e);
        throw e;
      }
    }
  }

  /**
   * List HL7v2 messages in HL7v2 Stores with optional filter.
   *
   * <p>This transform is optimized for splitting of message.list calls for large batches of
   * historical data and assumes rather continuous stream of sendTimes.
   *
   * <p>Note on Benchmarking: The default initial splitting on day will make more queries than
   * necessary when used with very small data sets (or very sparse data sets in the sendTime
   * dimension). If you are looking to get an accurate benchmark be sure to use sufficient volume of
   * data with messages that span sendTimes over a realistic time range (days)
   *
   * <p>Implementation includes overhead for:

View on GitHub (pinned to 12126d8942)