Overview
HL7 v2 separators carry structure, not decoration
In the commonly used delimited encoding, an HL7 v2 message is a hierarchy of segments, fields, repetitions, components and subcomponents. A separator character tells the parser where one structural value ends and the next begins. If the same character is intended as literal data, the sender must encode it with the matching HL7 escape sequence.
MSH|^~\&|SENDER|MAIN|RECEIVER|MAIN|20260728100000||ADT^A01^ADT_A01|MSG000301|P|2.5.1
PID|1||MRN100^^^MAIN^MR~NAT200^^^NATIONAL^NI||SAMPLE^ALEX
In this example, the vertical bar separates fields, the caret separates components, and the tilde separates two repetitions of PID-3. Those meanings come from the start of the MSH segment; they are not inferred from the PID segment.
Read MSH before parsing the rest of the message. The first separator after MSH defines MSH-1, and the following characters define the remaining encoding characters in MSH-2.
Delimiter hierarchy
What each delimiter separates
| Level | Common character | Declared by | Purpose |
|---|---|---|---|
| Segment | CR (0x0D) | HL7 encoding rules | Ends one segment and begins the next segment after its three-character ID. |
| Field | | | MSH-1 | Separates numbered fields such as PID-3 and PID-5. |
| Component | ^ | First character of MSH-2 | Separates components inside a composite value. |
| Repetition | ~ | Second character of MSH-2 | Separates repeated occurrences of a repeatable field. |
| Escape | \ | Third character of MSH-2 | Starts and ends an HL7 escape sequence. |
| Subcomponent | & | Fourth character of MSH-2 | Separates the smallest normally delimited parts of a composite value. |
| Truncation | # | Fifth character of current MSH-2 | Marks a value that was truncated when the applicable version and profile support it. |
HL7 v2.5.1 defines four encoding characters in MSH-2: component, repetition, escape and subcomponent, with recommended value ^~\&.[1] The current HL7 v2.9 reference defines a fifth truncation character and recommends ^~\&#.[2]
This difference is version-sensitive. Do not reject a valid v2.5.1 message because it has four encoding characters, and do not assume the fifth character has truncation semantics without checking MSH-12 and the applicable interface profile.
MLLP control bytes are outside this hierarchy
VT, FS and the trailing CR used by Minimal Lower Layer Protocol (MLLP) frame the complete message at the transport layer. They are not HL7 field or component delimiters. See MLLP Framing Explained when the failure concerns bytes outside the HL7 payload.
MSH parsing
MSH-1 and MSH-2 make the header a special case
Most segments can be separated into fields by splitting on the declared field separator and numbering the resulting values after the segment ID. MSH cannot be handled that way without an adjustment because its separator characters define the parsing rules themselves.
MSH|^~\&|SENDER|MAIN|RECEIVER|MAIN|...
The positions are:
| Characters | HL7 location | Meaning |
|---|---|---|
| MSH | Segment ID | Identifies the message header segment. |
| | | MSH-1 | The field separator used throughout this message. |
| ^~\& | MSH-2 | The component, repetition, escape and subcomponent characters for this v2.5.1 example. |
| SENDER | MSH-3 | Sending application. |
| MAIN | MSH-4 | Sending facility. |
A generic string split produces MSH as the first token and ^~\& as the second. If code labels the second token MSH-1, every later MSH field is shifted by one. A parser should extract MSH-1 from the character immediately after the segment ID, then read MSH-2 using that field separator.
Blank fields still preserve position
Two adjacent field separators represent an unpopulated position. They do not remove the field from the segment. In the common header fragment ...|20260728100000||ADT^A01..., the empty value between the two separators is still MSH-8; the following composite remains MSH-9.
Custom delimiters
A parser should follow MSH instead of assuming |^~\&
The standard recommends the familiar characters, but MSH can declare other permitted display characters. The following synthetic v2.5.1 header uses * as the field separator, $ for components, % for repetitions, ! for escapes and @ for subcomponents:
MSH*$%!@*SENDER*MAIN*RECEIVER*MAIN*20260728101500**ADT$A01$ADT_A01*MSG000302*P*2.5.1
A parser that splits this message on | will treat the entire MSH segment as one value. A parser that recognises the field separator but still splits MSH-9 on ^ will fail to identify ADT, A01 and ADT_A01.
Custom separators need exact agreement between systems. A receiver may constrain the base standard through its implementation profile and accept only the conventional characters. That is a profile or product rule; it does not justify ignoring the delimiters in a message that the interface otherwise claims to support.
Each delimiter must remain distinct
The field, component, repetition, escape and subcomponent functions cannot be distinguished if the same character is assigned to more than one role. Validate the MSH declarations before parsing clinical or administrative fields, and reject an ambiguous header with a specific diagnostic rather than continuing with shifted data.
Escape sequences
Encode a literal delimiter instead of changing the field structure
When a delimiter character is part of the value rather than the message structure, the sender must use the corresponding HL7 escape sequence. The current HL7 control rules state that delimiters included in a component must be escaped.[3]
| Default form | Decoded character | Purpose |
|---|---|---|
\F\ | Field separator | Represents a literal | when the default field separator is used. |
\S\ | Component separator | Represents a literal ^. |
\R\ | Repetition separator | Represents a literal ~. |
\E\ | Escape character | Represents a literal backslash when \ is the declared escape character. |
\T\ | Subcomponent separator | Represents a literal &. |
\P\ | Truncation character | Represents a literal truncation character in versions that declare it. |
The backslash in these examples is not fixed. It represents whichever escape character is declared in MSH-2. In the custom header above, ! is the escape character, so a literal field separator * is encoded as !F!, not \F\.
Other standard and local escape forms
HL7 also defines forms such as \H\ and \N\ for highlighting, \Xdddd...\ for hexadecimal data, and \Zdddd...\ for locally defined escapes. Escape sequences cannot be nested.[3] A local \Z...\ value is interoperable only when both systems have agreed what its data means.
Do not treat a programming-language escape such as \n, \r or \x7C as an HL7 escape merely because it contains a backslash. Application source code may use those notations while constructing a string, but the transmitted HL7 payload must follow the declared HL7 encoding rules.
Worked examples
How one unescaped character changes the parsed message
Literal field separator in a comment
The intended NTE-3 value is “Allergy noted: fish | shellfish”. Sending the literal vertical bar changes the segment structure:
Incorrect
NTE|1|L|Allergy noted: fish | shellfish
Parsed result
NTE-3 = Allergy noted: fish
NTE-4 = shellfish
With the default escape character, the correct encoding is:
NTE|1|L|Allergy noted: fish \F\ shellfish
The receiver parses the segment boundaries first and then decodes \F\ as a literal | inside NTE-3.
Literal repetition separator in free text
Incorrect
OBX|1|TX|COMMENT||Expected range 5~10
Correct
OBX|1|TX|COMMENT||Expected range 5\R\10
If OBX-5 is repeatable in the applicable message definition, the unescaped tilde creates two repetitions. Even where the receiving profile does not permit repetition, the parser still sees a structural delimiter and may reject or misrepresent the value.
Repeating identifiers are not escaped data
PID|1||MRN100^^^MAIN^MR~NAT200^^^NATIONAL^NI||SAMPLE^ALEX
The tilde is structural here. It separates two legitimate repetitions of PID-3. Escaping it as \R\ would collapse the content into one repetition containing a literal tilde, which changes the identifier model.
Processing order
Parse structure before decoding escaped delimiter values
A practical receiver sequence is:
- Remove or separately process transport framing such as MLLP.
- Identify the MSH segment and extract the field separator from the character after
MSH. - Read
MSH-2and validate its encoding characters against the declared version and profile. - Split the message into CR-terminated segments.
- Parse fields, repetitions, components and subcomponents using the declared characters.
- Decode escape sequences within the resulting data values according to their context.
- Apply message-profile, field-usage, data-type and terminology validation.
Decoding \F\ into | before field parsing can turn literal data into a structural separator. Conversely, never de-escaping means the application receives encoded text rather than the intended value. Preserve the original payload alongside the parsed representation so the transformation can be checked.
Common failures
Delimiter defects and their observable effects
| Symptom | Likely cause | What to verify |
|---|---|---|
| Every MSH field is off by one | The parser treated the first split token as MSH-1 and ignored the special position of the field separator. | How MSH-1, MSH-2 and MSH-3 are extracted. |
| The whole message appears as one segment | Segments use LF or visible text instead of the expected CR, or transport/log processing changed line endings. | Raw bytes between segment IDs. |
| A name, comment or address creates extra fields | A literal field separator was not encoded with the field escape. | Original value before encoding and the transmitted field. |
| One identifier becomes several repetitions | A literal repetition character was sent unescaped. | The declared repetition separator and field repeatability. |
| Message type or trigger event is blank | The parser hard-coded ^ although MSH-2 declared another component separator. | MSH-2 and the literal MSH-9 value. |
| Escapes remain visible to the application | The parser preserved structure but did not de-escape the final data values. | Processing stage at which escape decoding occurs. |
| Escaped text creates extra fields | The implementation decoded escapes before structural parsing. | Parse and de-escape order. |
| Message works in one engine but fails in another | One product tolerates undeclared defaults, malformed MSH-2 or non-standard line endings. | Raw message, declared version and both products' profiles. |
| A v2.5.1 message is rejected for a missing fifth encoding character | Current v2.9 MSH-2 rules were applied without considering MSH-12. | Version-specific MSH-2 length and profile constraints. |
Troubleshooting
A repeatable delimiter investigation
- Capture the exact transmitted payload. Use the bytes or unmodified message before a log viewer changes control characters or line endings.
- Separate transport from content. Remove MLLP boundaries without discarding the CR bytes that terminate HL7 segments.
- Inspect the first MSH characters. Record
MSH-1, the completeMSH-2value andMSH-12. - Check delimiter uniqueness. Confirm that no structural role reuses the field separator or another encoding character.
- Parse one failing segment manually. Mark fields first, then repetitions, components and subcomponents.
- Locate literal delimiter data. Compare the source application value with its transmitted HL7 encoding.
- Check escape boundaries. Each escape must use the declared escape character at both ends and must not contain another nested escape.
- Compare before and after de-escaping. Verify that structural positions stay unchanged while the final data value gains the intended literal character.
- Apply the interface profile. Confirm field repeatability, data type, supported escape forms and any delimiter constraints.
- Test the corrected payload independently. Use the same raw message on both sides before changing mapping or business logic.
The HL7 Message Formatter & Viewer reads common delimiters from MSH and exposes fields, repetitions, components and subcomponents. Use it with synthetic messages to confirm the structure before investigating downstream mappings.
Checklist
What to define in an HL7 v2 interface
- The supported HL7 version and message profile.
- Whether only the recommended delimiters are accepted or valid custom delimiters are supported.
- The expected segment separator and how copied or file-based messages are normalised.
- How MSH is parsed without shifting its field numbers.
- Which fields may repeat and how repetitions are represented in the destination model.
- Which standard escape sequences and local
\Z...\forms are supported. - Whether escape decoding occurs in the integration engine, receiving application or both.
- How invalid, incomplete or nested escape sequences are reported.
- How the original payload and parsed field locations are logged without retaining patient data.
- Which test cases cover literal field, component, repetition, escape and subcomponent characters.
FAQ
Frequently asked delimiter questions
Is the field separator always a vertical bar?
No. | is the recommended and overwhelmingly familiar value, but MSH-1 defines the field separator for the message. A receiving profile can constrain which characters it accepts.
Why is the vertical bar after MSH called MSH-1?
It is both the separator after the segment ID and the value of MSH-1. The following token contains MSH-2, so MSH needs special handling when fields are numbered.
Should an ampersand in free text be sent as \T\?
Yes when & is the declared subcomponent separator and the character is intended as literal data in a location where it would otherwise be interpreted structurally. Use the escape character declared by that message.
Can I replace every backslash escape before splitting fields?
No. Decoding a field escape before structural parsing can introduce a literal field separator that is then mistaken for structure. Parse using the declared separators first, then decode escapes inside the resulting values.
Does MSH-2 always have five characters?
No. HL7 v2.5.1 defines four encoding characters. The current v2.9 reference includes a fifth truncation character. Interpret the header according to MSH-12 and the applicable interface profile.
Primary references
HL7 Version 2 encoding sources
- HL7 Version 2.5.1, Chapter 2: Control: MSH-1, the four MSH-2 encoding characters, recommended values and escape-sequence rules for a widely deployed HL7 v2 version.
- HL7 Version 2+: MSH Message Header Segment: current MSH-1 and five-character MSH-2 definitions, including the truncation character.
- HL7 Version 2+: Control, Use of Escape Sequences in Fields: delimiter escapes, formatting and hexadecimal forms, local escapes, and the prohibition on nested escape sequences.
- HL7 Version 2.9: Introduction and Encoding Rules: field-delimited message structure, segment separation, character-set context and positional field processing.
Local implementation guides and interface agreements remain authoritative for supported versions, delimiter constraints, field repetitions, escape handling and receiving-system behaviour.