HL7 transport guide

MLLP Framing Explained: VT, FS, CR and Common HL7 Transport Failures

MLLP marks an HL7 v2 payload with a start byte and a two-byte terminator. If those bytes are missing, sent as printable text or handled incorrectly across TCP reads, the connection can succeed while the receiver continues waiting for a complete frame. This guide explains VT, FS and CR, TCP stream buffering, ACK framing, and the checks that separate transport success from application acceptance.

By Health Data ToolsPublished 23 July 2026Technically reviewed 24 July 2026Approx. 13-minute read
Use the interface contract. MLLP defines a small byte-framing convention. Connection lifecycle, TLS, character encoding, response timeouts, ACK expectations, retries, duplicate handling, and operational ownership still need to be agreed for each interface.

Overview

MLLP marks where one HL7 v2 payload begins and ends

HL7 v2 messages are commonly carried over TCP. TCP provides a reliable, ordered byte stream, but it does not preserve application-message boundaries. Minimal Lower Layer Protocol (MLLP) adds one start byte before the HL7 payload and a two-byte terminator after it, allowing the receiver to identify complete frames within that stream.

The standard frame is:

0x0B  +  HL7 payload  +  0x1C 0x0D
 VT                         FS   CR

The HL7 MLLP block format defines the start block as byte 0x0B, the end block as byte 0x1C, and the final carriage return as byte 0x0D.[1] These are control bytes, not the printable characters “VT”, “FS”, “CR”, “0B”, or “1C”.

Use the frame boundaries. Treat TCP as a stream of bytes. A complete MLLP frame is identified by its control bytes, not by socket reads, packets, line breaks or timing.

Frame anatomy

The four parts of an MLLP frame

PartByteCommon labelPurpose
Start block0x0BVTMarks the beginning of the framed HL7 payload.
HL7 payloadVariableMessage dataContains the encoded HL7 message, normally beginning with MSH and using CR between segments.
End block0x1CFSMarks the end of the payload data.
Frame terminator0x0DCRImmediately follows FS and completes the MLLP boundary.

The historical names can be misleading. Byte 0x0B is the ASCII Vertical Tab control and byte 0x1C is File Separator, but their job here is simply to delimit the MLLP block. They are not HL7 field separators and they do not describe the clinical message structure.

Do not substitute SOH, STX, ETX or EOT

The HL7 block-format description explicitly distinguishes 0x0B from SOH and STX and distinguishes 0x1C from ETX and EOT.[1] A receiver expecting MLLP will not normally treat a frame using those alternative control bytes as valid merely because another serial protocol uses them.

Carriage returns

The final HL7 segment CR and the MLLP terminator CR have different jobs

A normally encoded HL7 message uses carriage return 0x0D as its segment separator. The final HL7 segment therefore usually ends with a CR before the MLLP end block. MLLP then adds another CR after FS:

<VT>MSH|^~\&|SENDER|MAIN|RECEIVER|MAIN|...<CR>
PID|1||TEST123^^^MAIN^MR||SAMPLE^PATIENT<CR><FS><CR>

In hexadecimal, the end of that stream is commonly:

... 50 41 54 49 45 4E 54 0D 1C 0D
                           │  │  └─ MLLP terminator CR
                           │  └──── MLLP end block FS
                           └─────── HL7 segment CR

The two CR bytes are adjacent only through the intervening FS byte. Removing the payload's final segment CR can leave the last HL7 segment unterminated. Removing the CR after FS leaves an incomplete MLLP end boundary.

CRLF and LF are not interchangeable with CR

Text editors and operating systems often display lines using CRLF or LF. HL7 segment separation and the MLLP frame terminator use CR in the conventional encoding. Some receivers tolerate additional LF bytes, but tolerance is an implementation choice and can conceal a sender defect. Diagnose the raw bytes rather than relying on how a log viewer renders the text.

Representations

Visible tokens are useful for logs, but the wire needs control bytes

MLLP may be shown in several human-readable forms:

Display formExampleWhat it represents
Visible labels<VT>...<FS><CR>Documentation or diagnostic notation.
Escaped bytes\x0B...\x1C\rA programming-language or log representation.
Hexadecimal0B ... 1C 0DThe actual byte values in readable form.
Raw controlsUsually invisibleThe real control bytes carried in the stream.

If a configuration field sends the literal characters <VT>, \x0B, or 0B, the receiver sees printable text rather than byte 0x0B. Confirm whether the field expects raw bytes, escape notation, hexadecimal input, or ordinary text.

TCP stream handling

One TCP read is not necessarily one MLLP frame

TCP is defined as a reliable, in-order byte-stream service.[3] It does not tell the application that a particular read corresponds to one message. A receiver may obtain:

  • only part of the start and payload;
  • one complete MLLP frame;
  • the end of one frame and the start of another;
  • several complete frames in one read;
  • no application data while the connection remains open.

Correct stream processing appends received bytes to a buffer, searches for a complete 0x0B ... 0x1C 0x0D frame, extracts it, and then continues scanning any remaining bytes. The application should not assume that network packets, receive events, or buffer sizes preserve MLLP boundaries.

Multiple messages can share one connection

Some interfaces open a connection for one message and close it after the response. Others keep the connection open and exchange many request and response frames. Either approach can work when both sides agree. The frame boundary—not connection closure—should identify the payload unless the interface contract explicitly defines another behaviour.

Connection established does not mean message accepted

A successful TCP connection proves that a socket session was established. It does not prove that the receiver found a complete MLLP frame, parsed the HL7 payload, accepted the message, or completed application processing. Those are separate states with different evidence.

Advertisement

Acknowledgements

An HL7 ACK is another HL7 message inside its own MLLP frame

MLLP does not itself express AA, AE, AR, CA, CE or CR. When an HL7 acknowledgement is required, the receiver creates an HL7 response message and frames that response with its own VT–FS–CR boundary:

Sender → Receiver
0B MSH|...|ADT^A01|MSG000201|... 0D PID|... 0D 1C 0D

Receiver → Sender
0B MSH|...|ACK^A01|ACK000201|... 0D MSA|AA|MSG000201 0D 1C 0D

A complete response MLLP frame proves only that a framed response payload was returned. The acknowledgement meaning comes from the HL7 response fields, especially MSA-1 and MSA-2. The distinction between transport delivery and application acknowledgement is part of the HL7 model.[2]

For detailed AA/AE/AR and CA/CE/CR handling, see HL7 ACK Codes Explained.

Do not describe FS–CR as an ACK. It terminates a frame. An ACK is a separate HL7 response message, normally returned in a separate MLLP frame.

Worked examples

Three byte-level framing scenarios

1. One complete valid frame

Visible
<VT>MSH|^~\&|ADT|MAIN|EHR|MAIN|20260723100000||ADT^A01|MSG201|P|2.5.1<CR>
PID|1||TEST201^^^MAIN^MR||SAMPLE^PATIENT<CR><FS><CR>

Hex boundaries
0B ... 0D 1C 0D

The receiver can extract the bytes between VT and FS as one HL7 payload. The final CR after FS completes the frame terminator.

2. One frame split across three TCP reads

Read 1: 0B MSH|^~\&|ADT|MAIN|EHR|MAIN|...
Read 2: ...|ADT^A01|MSG202|P|2.5.1 0D PID|1||TEST202...
Read 3: ...|SAMPLE^PATIENT 0D 1C 0D

Nothing is wrong with this exchange. The receiver buffers all three reads and processes the message only after the complete end boundary arrives.

3. Two complete frames in one TCP read

0B MSH|...|MSG203|... 0D PID|... 0D 1C 0D
0B MSH|...|MSG204|... 0D PID|... 0D 1C 0D

The receiver should extract the first frame, preserve the remaining bytes, and then extract the second frame. Treating the entire read as one message would combine two payloads incorrectly.

Common failures

Framing defects that often appear as timeouts

FaultReceiver viewLikely symptom
Missing VTNo recognised frame startThe receiver waits, discards bytes, or closes the connection without an HL7 ACK.
Missing FSFrame never reaches its end markerThe receiver continues buffering until timeout or connection closure.
FS without trailing CRIncomplete MLLP terminatorStrict receivers wait for one more byte and never process the payload.
Literal “<VT>” textPrintable characters rather than byte 0x0BNo valid frame start is detected.
STX/ETX framingUnexpected control protocolThe receiver rejects or ignores the stream.
LF-only segmentsPayload may appear as one malformed segmentParsing failure, missing ACK, or application error.
Unexpected bytes between framesNoise outside valid boundariesDesynchronisation, discarded data, or connection reset.
Character conversionPayload bytes differ from sender intentDelimiter, text, length, or parsing errors despite valid outer framing.

From the sender's perspective, several of these faults look the same: the socket opens, bytes are written and no ACK arrives. Inspect the raw bytes on both endpoints before changing HL7 fields or receiver business rules.

Operational handling

A timeout creates an unknown outcome

No ACK may mean the receiver never saw a complete frame. It may also mean the receiver processed the message but the ACK was delayed, lost, malformed, written to a different connection, or not read correctly by the sender. The sender cannot safely infer “not processed” from silence alone.

Before retrying, define:

  • the connection, send and response timeout values;
  • whether the sender may reuse a connection after timeout;
  • whether retries retain or replace MSH-10;
  • how the receiver detects duplicate messages and business events;
  • which errors are immediately retryable;
  • how ambiguous outcomes are reconciled against receiver logs or application state.

A retry can repeat processing when the first message was accepted but its ACK was not received. Define duplicate detection, idempotency and reconciliation rules before automating retries.

TLS is separate from MLLP

MLLP does not encrypt the payload or authenticate the remote endpoint. It can be carried through a separately secured channel such as TLS, a private network, or a tunnel, but the security layer has its own certificates, trust, protocol versions, timeouts, and failure modes.

Troubleshooting

A practical investigation sequence

  1. Confirm the destination. Check host, port, environment, DNS resolution, route, firewall, and whether TLS is expected.
  2. Confirm raw start and end bytes. Verify 0B at the start and 1C 0D at the end using a hex view.
  3. Check the HL7 segment separators. Confirm CR between segments and a complete final segment.
  4. Separate stream chunks from frames. Reassemble all TCP data in order before judging the message incomplete.
  5. Inspect bytes outside boundaries. Look for a BOM, whitespace, LF, logging prefixes, length headers, or extra text.
  6. Extract the payload. Parse the unwrapped HL7 independently to determine whether framing or message content is failing.
  7. Inspect the response stream. Determine whether a complete framed ACK was returned and correlate its MSA-2.
  8. Compare both endpoints' logs. Establish the last state each side can prove rather than inferring from one log.

The receiver logs the message but the sender times out

Check whether the receiver generated an ACK, framed it correctly, flushed it to the socket, and sent it on the connection the sender is reading. Then check whether the sender waits for the full 1C 0D boundary, can process multiple reads, and accepts the returned HL7 message type.

A packet capture appears to show half a message

Review the complete TCP stream rather than one packet. TCP segmentation is not MLLP framing. The remainder may be carried in later packets or coalesced differently at the receiving API.

The same configuration works on one server but not another

Compare character encoding, newline conversion, socket-library defaults, TLS settings, proxy behaviour, idle timeouts and escape-sequence handling. Then capture the outbound bytes from each server; identical configuration labels do not prove identical bytes.

Checklist

What to define in an MLLP interface specification

  • Host, port, environment, network path, and whether TLS or another secure channel is required.
  • Exact framing bytes: start 0x0B, end 0x1C, trailing 0x0D.
  • HL7 character encoding and expected segment separator.
  • One-message-per-connection or persistent-connection behaviour.
  • Whether multiple outstanding messages are permitted on one connection.
  • Expected HL7 response type and acknowledgement mode.
  • Connection, write, read, application-response, and idle timeouts.
  • Connection closure and reconnect behaviour after success, error, or timeout.
  • Retry limits, backoff, duplicate detection, and idempotency rules.
  • Maximum message size and receiver buffering limits.
  • Logging requirements for raw-safe diagnostics, control IDs, timestamps, and endpoint state.
  • Ownership and escalation process for ambiguous delivery outcomes.

FAQ

Frequently asked MLLP questions

Is the start byte VT or vertical tab?

Both labels commonly refer to byte 0x0B. In MLLP it functions as the start block character; it is not being used to format text vertically.

Does FS alone end the frame?

No. The standard MLLP end boundary is FS 0x1C immediately followed by CR 0x0D.

Can one TCP read contain two messages?

Yes. TCP is a byte stream. A read may contain part of a frame, one frame, or several frames. The application must parse the MLLP boundaries.

Does a successful socket write prove delivery?

No. It proves only what the local socket API reports. It does not prove that the remote application extracted, parsed, accepted, or processed the HL7 payload.

Is an HL7 ACK part of the same frame?

No. The request is one framed HL7 message and the ACK is normally a separate framed HL7 response message travelling in the opposite direction.

Does MLLP provide encryption?

No. Security must be provided separately through TLS, network controls, tunnels, or another agreed mechanism.

Advertisement

Primary references

Standards and protocol sources

  1. HL7 Version 2+ Minimal Lower Layer Protocol — block format definitions for start block 0x0B, payload data, end block 0x1C, and trailing carriage return 0x0D.
  2. HL7 Version 2.1, Chapter 1: Overview — relationship between HL7 application messages, lower-layer protocols, encoding rules, and application acknowledgements.
  3. RFC 9293: Transmission Control Protocol — current Internet Standard describing TCP's reliable, in-order byte-stream service.
  4. HL7 Version 2.8.1, Chapter 2: Control — message processing and acknowledgement behaviour carried above the transport framing.
  5. HL7 Version 2 product information.

Local interface agreements and vendor implementation guides remain authoritative for connection management, security, encoding, timeouts, acknowledgements, and retry behaviour.