Decode diagnostics
When @boarteam/fix finds a problem it returns it as data — a stable code, a severity, and where in the message it applies. It never throws. This is the complete catalogue: all 82 codes of KnownIssueCode in @boarteam/fix 0.6.1 — the list is generated from the package, the explanations are ours. For how to produce and assert on them in your own code, see the parsing and validation docs.
parse/* — wire decoding
Raised by parse and parseAll while decoding wire bytes: framing, field structure, repeating groups and value coercion. The message still decodes as far as the bytes allow — the damage arrives as data, never an exception.
| Code | Severity | What it means | Usual cause |
|---|---|---|---|
| parse/empty-input | info | The input contained no FIX fields at all. | An empty string or buffer, or input that is nothing but separators. |
| parse/malformed-field | warning | A segment had no usable tag=value shape; it was reported and dropped. | A log line torn mid-field, or non-FIX text mixed into the capture. |
| parse/missing-begin-string | error | No BeginString (8) field was found. | A fragment cut before the envelope, or a separator the tokenizer was not told about. |
| parse/missing-body-length | error | No BodyLength field was found. | The message was cut short, or the separator was not what the decoder expected. |
| parse/missing-msgtype | error | No MsgType (35) field was found. | Not a FIX message, or only a fragment of one. |
| parse/framing-order | error | BeginString, BodyLength and MsgType are present but not in wire positions one, two and three. | A hand-assembled message, or fields reordered by whatever produced the log. |
| parse/begin-string-mismatch | warning | BeginString names a different version than the dictionary in use. | Decoding a FIX 4.2 message against the FIX 4.4 dictionary, or vice versa. |
| parse/unknown-msgtype | error | The MsgType is not in the dictionary, so the message was parsed flat — fields kept, no group structure. | A venue-specific message, or the wrong dialect for the traffic. |
| parse/missing-checksum | error | No CheckSum field was found. | The message was cut short, or the separator was not what the decoder expected. |
| parse/checksum-mismatch | error | The CheckSum field does not match the bytes. | Usually a message that was edited, or one copied with pipes after being framed with SOH — the two forms have different checksums. |
| parse/body-length-mismatch | warning | BodyLength disagrees with the framed body. | Truncated capture, or a hand-written message where the length was not recomputed. |
| parse/unknown-tag | info | A tag is not defined in the dictionary; the field was kept at the top level. | A venue extension the dictionary does not carry — defineExtension teaches it the tag. |
| parse/tag-not-in-message | warning | The tag exists in the dictionary but not in this message's definition; kept at the top level. | A field borrowed from another message type, or a venue placement the dictionary does not know about. |
| parse/duplicate-tag | error | The same tag appeared more than once in one scope; the first value was kept. | A repeating group whose counter went missing, flattening entries into duplicates at one level. |
| parse/duplicate-group | warning | The same group counter opened twice in one scope; the first group's entries were kept. | A message stitched together from fragments, or group entries emitted in two separate runs. |
| parse/invalid-group-count | error | A group counter's value is not a whole number; it was treated as zero. | An empty or corrupted counter value — the parsed entries then also raise a count mismatch. |
| parse/group-count-mismatch | warning | A group counter promised one number of entries and the wire carried another. | A truncated message, or entries added or removed without recomputing the counter. |
| parse/data-length-mismatch | — | Reserved in the catalogue; no current code path emits it. | Length/data disagreements surface as validate/conditional-required (a data field present without its Length companion) instead. |
| parse/unknown-datatype | warning | A field's declared datatype is not in the dictionary; the value was left as a string. | A hand-edited or generated dictionary naming a type it never defines — validateDictionary catches this ahead of time. |
| parse/invalid-int | error | An int-family field held something that is not a whole number. | An empty value, a placeholder like N/A, or a locale-formatted number with a comma. |
| parse/invalid-float | error | A float-family field held something that is not a number. | Same causes as above. |
| parse/invalid-boolean | error | A Boolean field held something other than Y or N. | true/false or 1/0 from a system that does not speak FIX Booleans. |
| parse/number-precision | warning | An integer is lexically valid but exceeds JavaScript's safe range; the exact digits were kept as a string. | An identifier packed into an int field with more digits than a double can hold. |
validate/* — dictionary conformance
Raised by validate over an already-parsed message: presence of required members, enum membership, value formats, and conditional rules. When validating a FIXT transport/application pair, every issue additionally carries a session or application layer.
| Code | Severity | What it means | Usual cause |
|---|---|---|---|
| validate/unknown-msgtype | warning | The MsgType is not in the dictionary, so presence and conditional rules were not checked. | A venue-specific message, or the wrong dialect — per-field value checks still ran. |
| validate/required-field-missing | error | A field marked required for this message is absent. | A partially-built message, or one built against a different version. |
| validate/required-group-missing | error | A required repeating group has no entries. | Group members supplied at the wrong nesting level are silently dropped when encoding — this is how that shows up. |
| validate/empty-value | error | A field is present but its value is empty. | A builder that emitted tag= with nothing after it — FIX forbids a tag without a value. |
| validate/value-not-in-enum | error | An enumerated field held a value the dictionary does not define. | A venue extension, or a value that exists in a different FIX version. |
| validate/invalid-value | error / warning | A value did not match its datatype's format. | Most often a timestamp in ISO format instead of the FIX YYYYMMDD-HH:MM:SS[.sss] form. The ISO country/currency/language checks report as warnings — those sets are heuristics, not dictionary data. |
| validate/conditional-required | error | A field required by a condition is absent. | Setting PossDupFlag=Y without OrigSendingTime is the classic case; a data field present without its Length companion is the other built-in. |
| validate/field-outside-layer | error | An application-layer field appeared on a session (admin) message. | Only reported when validating a FIXT transport/application pair — business fields do not belong on Logon or Heartbeat traffic. |
dict/* — dictionary integrity
Raised by validateDictionary over a DictionaryJSON: the internal consistency of a dictionary before the engine runs on it. The shipped dictionaries pass clean — these appear when loading hand-built, merged or extended ones.
| Code | Severity | What it means | Usual cause |
|---|---|---|---|
| dict/missing-version | error | The dictionary has no version identifier. | A hand-built DictionaryJSON missing its top-level version field. |
| dict/missing-begin-string | error | The dictionary has no beginString. | Same as above — framing needs the tag 8 value to check against. |
| dict/missing-datatypes | error | The datatypes collection is missing or not an object. | A truncated or hand-built dictionary; datatype and field-type checks were skipped. |
| dict/missing-fields | error | The fields collection is missing or not an object. | Same shape problem; field-level checks were skipped. |
| dict/missing-components | error | The components collection is missing or not an object. | Same shape problem; member walks were skipped. |
| dict/missing-messages | error | The messages collection is missing or not an array. | Same shape problem; message-level checks were skipped. |
| dict/datatype-cycle | error | A datatype's parent chain loops back on itself. | An edit that made a type its own ancestor — the derivation tree must reach a root. |
| dict/datatype-missing-parent | error | A datatype names a parent that does not exist. | A pruned dictionary that dropped a base type but kept its children. |
| dict/datatype-bad-base | error | A datatype's base is not one of the five roots: int, float, char, String, data. | A typo, or a derived type name written where the root belongs. |
| dict/field-key-mismatch | error | A field is stored under a key that is not its own tag. | Hand-editing the fields record without updating the key it sits under. |
| dict/field-bad-tag | error | A field's tag is not a positive integer. | A zero, negative or non-numeric tag from a generator or edit. |
| dict/field-unknown-type | error | A field names a datatype the dictionary does not define. | A pruned datatypes table, or a typo in the type name. |
| dict/duplicate-field-name | error | Two tags share one field name. | Merging dictionaries or extensions without renaming — name lookups become ambiguous. |
| dict/duplicate-enum-value | warning | A field lists the same enum value more than once. | A merge that concatenated value tables instead of unioning them. |
| dict/message-missing-msgtype | error | A message has no MsgType value. | A hand-built message entry that never set msgType. |
| dict/duplicate-msgtype | warning | Two messages claim one MsgType; only the first is reachable. | A merge that appended a redefinition instead of replacing the original. |
| dict/duplicate-message-name | warning | Two messages share one name; lookups by name are ambiguous. | Same merge problem as above, on the name side. |
| dict/unknown-field-ref | error | A message, component or group body references a field tag that does not exist. | A pruned fields table, or a generator emitting references it never defined. |
| dict/unknown-component-ref | error | A body references a component that does not exist. | Same as above, on the component side — the walk stops at the missing reference. |
| dict/component-cycle | error | Component references form a cycle, which can never be expanded. | A component that includes itself through intermediaries. |
| dict/unknown-group-counter | error | A repeating group's counter tag is not a defined field. | The counter was renumbered or dropped while the group kept pointing at it. |
| dict/non-counter-group-head | warning | A group's counter field exists but is not a NumInGroup type. | A plain int heading a group — parse-time counter detection depends on the datatype. |
| dict/empty-group | warning | A repeating group has no members. | An edit that emptied the body; there is nothing for entries to consist of. |
| dict/unresolvable-group-delimiter | error | A group's body resolves to no leading wire field, so entries have no delimiter. | A body starting with components that themselves resolve to nothing — the parser could never find entry boundaries. |
extend/* — applying extensions
Raised by extendDictionary while applying a venue extension declaration. Severity encodes the outcome: error means the operation was skipped or reverted, warning means it was applied but is worth review (or, for extend/duplicate-member, was already satisfied), info is advisory.
| Code | Severity | What it means | Usual cause |
|---|---|---|---|
| extend/field-tag-collision | warning | The extension redefined an existing tag; the extension's definition replaced the base one. | A deliberate override, or accidental tag reuse — an identical redefinition is applied silently. |
| extend/field-name-collision | error | The extension field's name is already bound to a different tag; the field was skipped. | Pick a unique name, or redefine the existing tag itself. |
| extend/field-bad-tag | error | The extension field's tag is not a positive integer; the field was skipped. | A typo in the declaration. |
| extend/field-unknown-type | error | The extension field names a datatype the dictionary does not define; the field was skipped. | Extensions cannot introduce datatypes — the type must already exist in the base. |
| extend/tag-outside-user-range | info | A new tag lies outside the user-defined ranges (5000–9999 and 20000+). | Venues do this in practice — cTrader's 1007/1008, for instance. It may collide with standard tags of other FIX versions; advisory only. |
| extend/data-length-unwired | warning | A data-typed field was added without a lengthField companion. | Without a Length partner, a value that embeds the separator cannot be scanned safely. |
| extend/enum-unknown-field | error | Enum values target a field that does not exist; the entry was skipped. | A typo, or a field that belongs to a different dialect. |
| extend/enum-value-conflict | warning | An added enum value already exists on the field under a different name; the extension's entry replaced it. | Renaming a stock value — fine when deliberate; an identical value+name pair is deduplicated silently. |
| extend/component-collision | error | A new component uses a name that already exists; the definition was skipped. | Use the append form to extend the existing component instead of redefining it. |
| extend/component-cycle | error | The addition would create a component reference cycle; it was skipped or reverted. | A component that ends up including itself through the placement. |
| extend/msgtype-collision | warning | A new message reuses an existing MsgType; it replaced the original in place. | A deliberate message override — MsgType lookup is first-wins, so in-place replacement is the only semantics that works. |
| extend/message-name-collision | warning | The new message's name is already used by a message with a different MsgType; both were kept. | Name-based lookups — including extension patch targeting — become ambiguous. |
| extend/header-trailer-injected | info | The dictionary's standard header and trailer components were wrapped around the new message's body. | Normal and informational: new messages declare only their body. |
| extend/header-trailer-missing | warning | No standard header or trailer component could be detected; the message was applied without an envelope. | A base dictionary without recognisable header/trailer components — encode will silently drop session fields unless the body carries them. |
| extend/target-not-found | error | A placement's target — component, message, or group path — could not be resolved; the placement was skipped. | A typo, an ambiguous message name, or a group that lives behind a shared component: target that component explicitly. |
| extend/unknown-member | error | A member spec names a field, component or group counter that exists nowhere; the whole placement was skipped. | Placements are atomic — one unresolvable name skips the list rather than half-applying it. |
| extend/member-not-found | error | The after anchor matches no member of the target body; the placement was skipped. | The anchor member was renamed or lives in a different scope than the placement targets. |
| extend/duplicate-member | warning | The placed member is already part of the target scope; it was not added again. | Re-applying an extension — extendDictionary is idempotent, and this is the expected sign of a double application. |
| extend/counter-not-marked | warning | A placed group's counter field does not derive from NumInGroup. | Fix the field's type — parse-time counter detection depends on it. |
| extend/group-delimiter-shift | error | The placement would change some repeating group's entry delimiter; everything it inserted was reverted. | Inserting ahead of a group's first field — entry detection on real traffic would break. |
| extend/ambiguous-boundary | error | The member could sit where an open repeating group could claim it on re-parse; that member was skipped. | Anchor it ahead of the group with after, or place it inside the group instead. |
| extend/unresolvable-group-delimiter | error | A new group's body resolves to no leading wire field; the placement was skipped. | The same defect dict/unresolvable-group-delimiter catches, refused before it can enter the dictionary. |
| extend/data-length-not-placed | warning | A data field was placed without its Length companion positioned before it in the same body. | Add the Length member ahead of it — encode cannot emit the length, and an embedded separator would corrupt the frame. |
| extend/counter-as-field | warning | A NumInGroup counter was placed as a plain scalar member. | Use the group form with members instead — on the wire this field heads a repeating group, and real traffic would mis-parse. |
| extend/invalid-spec | error | An extension entry is structurally malformed; it was skipped. | A missing members array or a non-object entry — the issue message names the exact path. |
| extend/delimiter-defined | info | A group that previously had no resolvable entry delimiter now has one. | Usually the fix for dict/unresolvable-group-delimiter landing; advisory. |
| extend/component-fanout | info | A component placement is visible in every scope that references the component. | Component reuse is the FIX model; the issue lists the affected messages so the fan-out is a known fact, not a surprise. |
Severity
Issues carry one of three severities. error means the message is wrong in a way that matters; warning means it is suspect but usable; info is a note. A message can be framed: true and still carry errors — framing only means the envelope was found, not that it checked out, so “framed” is never the same as “valid”. For the extend/* family the severity additionally encodes the outcome of the operation, as the section above describes; parse/data-length-mismatch is declared in the catalogue but currently emitted by no code path, and its row says so rather than inventing behaviour.