Skip to main content
Version: v0.1.0

Warning and fatal codes

Every deviation the parser notices lands in one of two places, and the split is the whole tolerance model:

  • A warning is data on msg.warnings. The parse still succeeded and the value is still there. Twenty codes, listed below, exported as WARNING_CODES.
  • A fatal is a thrown Hl7ParseError. The input is not recoverable as HL7 at all. Four codes, listed below, exported as FATAL_CODES.

Both registries are a public, versioned contract. A code is never renamed under you without a breaking release, so w.code === WARNING_CODES.UNKNOWN_SEGMENT is safe to branch on and safe to persist. Prefer logging w.code and w.position over w.message: the codes are stable, and the messages are bounded but not absolutely content-free (see Troubleshooting).

import { parseHL7, WARNING_CODES } from "@cosyte/hl7";

// A ZZZ segment is not a standard HL7 segment: tolerated, and surfaced as a warning.
const raw = "MSH|^~\\&|LAB|MAIN|EHR|REF|20260419143000||ADT^A01|EX00003|P|2.5\rZZZ|1";

const msg = parseHL7(raw);

msg.warnings.some((w) => w.code === WARNING_CODES.UNKNOWN_SEGMENT); // => true

Warnings​

Emitted by the parser unless the row says otherwise. Under { strict: true } every one of them is promoted to a thrown Hl7ParseError instead.

CodeWhat it means
MLLP_FRAMING_STRIPPEDThe input arrived wrapped in MLLP transport framing bytes. They were removed before parsing and the message itself is untouched.
FIELD_WHITESPACE_TRIMMEDLeading or trailing whitespace was trimmed from a field value. The warning carries only the character counts, never the value.
UNKNOWN_ESCAPE_SEQUENCEAn escape was not recognisable HL7 escape grammar, or ran to end of input unterminated. It is preserved verbatim in the parsed value.
TIMESTAMP_FALLBACK_FORMATA timestamp did not match the strict HL7 shape, but a declared or built-in fallback format resolved it. Not produced by a parse: see below.
SEGMENT_CASEA segment identifier carried a lowercase letter. It resolves as the segment it names, and the spelling that arrived is re-emitted unchanged.
EXTRA_FIELDSA segment carries more fields than the active profile declares. The extras are preserved on the raw segment rather than dropped.
UNKNOWN_SEGMENTNo standard segment carries that name, compared ignoring case, and no active profile claims it as a custom segment either.
DUPLICATE_REQUIRED_SEGMENTA segment the active profile marks as a singleton appears more than once. Both copies are kept for you to reconcile.
ENCODING_MISMATCHThe encoding characters declared in MSH-2 disagree with the separators the later segments actually used.
MISSING_REQUIRED_FIELDA field the active profile marks as required is absent or empty. Distinct from the fatal raised when MSH itself is missing.
MISSING_EXPECTED_GROUPThe published structure for this message type gives a minimum of one of some segment or group, and the message carries none of it.
OUT_OF_ORDER_SEGMENTA segment appears outside the order the active profile declares for it. Nothing is reordered; the deviation is reported.
VERSION_MISMATCHMSH-12 declares an HL7 version that differs from the one a profile or the parse options explicitly expected.
UNKNOWN_CHARSETMSH-18 or a charset override names a value that HL7 Table 0211 does not contain. Bytes are read as latin1 rather than guessed at.
UNSUPPORTED_CHARSETA recognised Table 0211 set was not decoded, either because it is out of scope here or because a strict decode of it failed.
ACK_NO_CORRELATION_IDThe inbound message carried no MSH-10 control id, so the built acknowledgement leaves MSA-2 empty instead of inventing one.
MERGE_MISSING_PRIOR_OR_SURVIVORAn identity merge or move event is missing one side of the prior and surviving pair, or that side carries no usable identifier.
BATCH_COUNT_MISMATCHA batch or file trailer declares a count that differs from what the splitter found. Nothing is dropped to make the numbers agree.
BATCH_MISSING_TRAILERAn envelope header opened a scope that no matching trailer segment ever closed. The split still returns every message it found.
UNTERMINATED_STREAM_MESSAGEThe last message in a stream ended with no segment terminator. It is still yielded in full, but the feed may have been cut off.

Three of these are emitted by a surface other than parseHL7, and arrive on that surface's own result rather than on msg.warnings: ACK_NO_CORRELATION_ID from the acknowledgement builder, MERGE_MISSING_PRIOR_OR_SURVIVOR from identityEvents(), and the two batch codes plus UNTERMINATED_STREAM_MESSAGE from splitBatch() and parseStream().

TIMESTAMP_FALLBACK_FORMAT is a fourth exception, of a different kind: it is a published, stable code with no emit site a parse reaches, so it does not appear on msg.warnings at all. Read matchedFormat on the timestamp instead. It names the format that resolved a non-canonical value, and is absent when the value was canonical HL7 and parsed strictly. The code is documented here because it is exported and would otherwise be an undocumented member of WARNING_CODES; do not write a check that waits for it to arrive.

Fatals​

Thrown as Hl7ParseError regardless of mode. They mean the bytes are not recoverable as HL7, not that the sender is quirky.

CodeWhat it means
NO_MSH_SEGMENTThe input carries no MSH header, so there are no delimiters and no message type to read, and nothing that can be parsed.
MSH_TOO_SHORTThe MSH header is truncated before the fields delimiter discovery needs, so the rest of the message cannot be tokenized.
INVALID_ENCODING_CHARACTERSMSH-1 and MSH-2 do not yield a usable delimiter set, so every field boundary in the message would have to be a guess.
EMPTY_INPUTThe input was empty, or held nothing but whitespace, so there is no message present to parse in the first place.

Catch the error and read its code to tell them apart:

import { parseHL7, FATAL_CODES, Hl7ParseError } from "@cosyte/hl7";

let matched = false;
try {
parseHL7("");
} catch (err) {
matched = err instanceof Hl7ParseError && err.code === FATAL_CODES.EMPTY_INPUT;
}

matched; // => true

Hl7ParseError.snippet carries up to 40 characters of the raw input verbatim and the library never redacts it. It is the field to scrub at your logging edge.