Interface: UndefinedVrFinding
One element that was emptied because its on-wire VR is not one of the 34 PS3.5 section 6.2 defines, so nothing this library did to its bytes counts as decoding a Value Field.
Why such an element exists at all
Under an Explicit VR Transfer Syntax the VR is two bytes the sender wrote, and this parser trusts them (Postel's Law on the read path). The routine way two arbitrary bytes end up in a VR field is an under-declared Value Length upstream: the reader finishes the short value, and the leftover bytes of the value that was actually encoded are read as the next Data Element header. Tag, VR and length are then all fragments of somebody's value, and the element that genuinely followed is consumed as this fabricated element's "value".
Measured on scripts/measure-sq-bound-grid.ts: a carrier under-declaring by 6
produces (4156,554C) with the VR bytes "E ", whose value holds the source
(0010,0020) Patient ID in full. It reaches string carriers exactly as it
reaches binary ones, because the carrier's own VR is not what decides it.
Why emptying, and why it is not a guess
PS3.5 2026c section 6.2 requires every VR not yet defined to use the long-form Data Element Structure - "with reserved bytes after the VR and a 32-bit unsigned integer VL" - so an unrecognized VR read short-form, which is what this parser does, is by the standard's own structure rule not a reading of a Value Field. There is nothing to prove about the content: the test is a membership check against the closed 34-VR set on a field the parser already recorded, so there is no scan, no per-offset loop, and no cost that follows an attacker-chosen value length.
PS3.15 2026c section E.1.1 obliges an implementation claiming the Basic Application Level Confidentiality Profile to "protect or retain all instances of the Attributes listed in [Table E.1-1]". Those instances cannot be reached inside bytes that were never a value, so the obligation falls on the carrier.
UN is not this. UN is one of the 34, so an ordinary unknown-VR element
- the Implicit VR fallback for a tag this build's dictionary does not publish,
and the CP-246 shape - never reaches here. That is the line the sibling
SQ-with-no-items rule could not draw.
Why this finding names no tag, when every sibling finding does
Because the tag may be content, and nothing here can tell. The paragraph
above is the whole argument: when an under-declare desynchronized the reader,
the four tag bytes and the two VR bytes were read out of the middle of some
element's Value Field, so reporting the "tag" would republish four bytes of
the document. An unrecognized VR written honestly, at a correct length, raises
this same code and has an ordinary tag - and the two are indistinguishable
here, so the tag is withheld on both routes rather than on a guess. Measured on a synthetic ST carrier holding
"MR BRAIN SMITHSON", the fabricated tag is 48544F53 - four bytes of the
surname. EmbeddedAttributeFinding and
UnauditableSequenceFinding may carry a tag because theirs came from a
header the sender really wrote; this one may not, and the asymmetry is the
finding rather than an inconsistency.
byteOffset locates the element instead - a position this parser counted.
Nothing here renders a document byte: an offset the parser counted, a decoded
length, and the structural contextPath. Safe to log.
Example
import { deidentify, parseDicom } from "@cosyte/dicom";
const { report } = deidentify(parseDicom(buf));
for (const u of report.undefinedVrElements) {
console.warn(`offset ${String(u.byteOffset)}: ${String(u.byteLength)} bytes dropped`);
}
Properties
byteLength
readonlybyteLength:number
Byte length of the value field that was dropped.
An input-derived count, and on this finding specifically that is not a
formality. For the sibling findings the length came from a header the
sender wrote; here the two length bytes can themselves be value bytes, like
the tag bytes. What is published is the number they decode to, never the
bytes - the same footing as every {n} in the warning registry, which is
documented there as "an input-derived count". A number is not a rendering,
and the reach is at most a character or so, but do not describe this field
as "structural, never a value" the way its siblings are described.
byteOffset
readonlybyteOffset:number
Byte offset of the emptied element's header. This is how the element is
identified, and there is deliberately no tag field - see the note above:
a fabricated header's tag bytes are part of some element's value.
contextPath?
readonlyoptionalcontextPath?: readonlystring[]
Tag/index chain when the carrier is inside a sequence item; omitted at the root.