HL7 v2 message validation

Check messages against the HL7 2.9 specification - required fields, datatypes and vendor quirks.

Validation runs two ways - during parsing, where the first problem raises an exception, or standalone with validate_message, which collects every problem into one result. Both check the same rules from the HL7 2.9 specification: required fields must be present and field values must match their declared datatypes. This page covers both, plus the tolerance switches that repair almost-correct vendor messages before the checks run.

Validation during parsing

Parsing validates by default - the first field-level problem raises HL7ValidationError:

# Zato
from zato.hl7v2 import parse_hl7, HL7ValidationError

raw = (
    'MSH|^~\\&|SENDER|FACILITY|RECEIVER|FAC|20260315||ADT^A01^ADT_A01|CTL001|P|2.9\r'
    'EVN|A01\r'    # <- EVN-2 (recorded date/time) is required but missing
    'PID|1||12345^^^HOSP^MR||SMITH^JOHN||19800115|M\r'
    'PV1|1|I\r'
)

try:
    message = parse_hl7(raw)
except HL7ValidationError as e:
    ...    # Message validation failed: EVN.1.2: Missing required field EVN.2

Pass validate=False to accept any message that parses, which is the usual choice for real-world feeds that do not strictly follow the specification:

message = parse_hl7(raw, validate=False)

The parser itself is lenient - a message missing a whole segment still parses, and segments the structure does not declare are skipped. The one structural error it does raise is ValueError for an MSH-9 that names no known message structure:

raw = 'MSH|^~\\&|SENDER|FACILITY|RECEIVER|FAC|20260315||QQQ^Z99^QQQ_Z99|CTL001|P|2.9\r'

parse_hl7(raw, validate=False)
# ValueError: Parse error: Unknown message structure

Collect every problem at once

Use validate_message when one exception per message is not enough - it returns a ValidationResult with is_valid, errors and warnings, and every problem is in the list:

# Zato
from zato.hl7v2 import validate_message

result = validate_message(raw)

if not result.is_valid:
    for error in result.errors:
        print(error.path)       # 'EVN.1.2'
        print(error.code)       # 'REQUIRED_FIELD'
        print(error.message)    # 'Missing required field EVN.EVN.2 (EVN.2)'

The result is truthy when the message is valid, so if validate_message(raw): works too.

Datatype problems are reported the same way - a non-numeric value in a field declared as SI (sequence ID) produces an error with the code INVALID_DATATYPE and the message Expected datatype 'SI', got 'XXXXXXXXXX'.

Tolerance for vendor quirks

Vendor systems routinely send messages that are almost correct - carriage returns embedded in field values, invalid escape sequences, placeholder text. A ToleranceConfig repairs such input before parsing and validation run, so both can pass without changing what the sender emits:

# Zato
from zato.hl7v2 import parse_hl7, ToleranceConfig

tolerance = ToleranceConfig()
tolerance.strip_embedded_cr_from_fields = True
tolerance.normalize_invalid_escape_sequences = True

message = parse_hl7(raw, validate=True, tolerance=tolerance)

The available switches include, among others:

  • normalize_invalid_escape_sequences - fix backslash sequences that are not valid HL7 escapes
  • strip_embedded_cr_from_fields - remove carriage returns embedded inside field values
  • normalize_unescaped_delimiters - handle delimiter characters used as literal text
  • force_standard_delimiters - rewrite non-standard delimiter declarations to ^~\&
  • strip_placeholder_text_from_fields - drop placeholder values such as "" markers, with the patterns configurable through placeholder_patterns
  • normalize_obx2_value_type and replace_invalid_obx2_value_type - repair OBX-2 value types
  • coded_field_mappings - map vendor-specific codes to standard ones per field

MLLP channels expose the same switches in their configuration, so the identical repairs can run on traffic received over the wire - the channel configuration reference lists them all with their defaults.

See also

PageWhat it covers
Parsing and serializationparse_hl7 and the validate flag in context
Field accessReading and writing the fields validation checks
Channel configurationThe same tolerance switches on MLLP channels

Learn more


Schedule a meaningful demo

Book a demo with an expert who will help you build meaningful systems that match your ambitions

"We evaluated 12 integration platforms and Zato was the only one to score 100%."

Philip Zuñiga, Assistant Professor, University of the Philippines