Sep 9, 2026
DICOM / PACS Vendor-Neutral Archive
How it works
The tempting places to aim test effort in this chain are the ends: the modality fleet, where pixels originate, and the Web Viewer API, where a radiologist finally sees them. Both are wrong first choices, because both are the parts of the flow that are most constrained by what they talk to. A modality emits studies in a shape its own vendor controls; the viewer API renders whatever the transcoder hands it. Neither has to reconcile anything. The Vendor-Neutral Archive does, and that is the whole point of it existing — it sits downstream of a Site PACS that has already applied its own conventions to a heterogeneous fleet, and it is asked to hold all of that as if it were one coherent corpus. Neutrality is not a property the archive receives; it is work the archive performs, and work performed is where defects live.
That makes the VNA-to-transcoder seam the first place a QA engineer should stand. The archive normalizes across modality-vendor variation and the Site PACS's handling of it; the Format Transcoder then re-derives a viewer-consumable representation from that normalized form. Every assumption the VNA made about what a study "really" is gets cashed out by the transcoder, and any gap between them surfaces not as a crash but as an image that renders — just subtly wrong, or wrong for one modality's studies only. A study that survives a modality and displays in the viewer has proven very little; it has proven the endpoints did their narrow jobs. What it has not proven is that the archive's normalization and the transcoder's interpretation of that normalization agree, per modality, per PACS convention. Test the middle with studies drawn from across the fleet, compare what the transcoder emits against what the archive claims to hold, and the endpoints will mostly take care of themselves.
Caveats — what breaks in practice
The failure most likely to blindside a team is the success response returned for a record that was never actually persisted. Every other item on the list announces itself: a poison message halts processing, an oversized batch times out, throttling under batch load slows things visibly, validation rejects records with edge-case values and produces a rejection you can count. Even the ugly ones — corrupt or truncated readings, missing segments, non-standard field usage per site — surface as something wrong downstream. A false success surfaces as nothing at all. The interface reports health, the send-side metrics look clean, and the study is simply absent from the archive, discovered weeks later by a clinician rather than by a monitor. And it has company on this list that makes it worse: partial writes on failure and name collisions that overwrite earlier payloads both produce the same silence, where the acknowledgment was honest about receipt and wrong about durability.
The assumption is that an acknowledgment is a durability guarantee — that a 200 or a positive DICOM/HL7 ack means the object is committed and retrievable. That assumption is entirely reasonable, which is exactly why it hurts; it is also the assumption that the rest of the pipeline quietly depends on, because processing triggered before the write completes only makes sense if you believe the ack and the commit are the same event. For QE, this means acknowledgment-based assertions are not sufficient test oracles. Verification has to read back the archived object independently after the ack, confirm it against what was sent, and specifically cover the paths where a partial write or a name collision would leave an ack intact and the payload gone. Test the reconciliation gap between "accepted" and "retrievable," not just the response code, and include duplicate messages on interface restarts in that read-back check, since a retry that succeeds on the second attempt can mask a first attempt that lied.
How to test this end to end
Picture a chest CT acquired on a Siemens SOMATOM at Riverside Imaging: StudyInstanceUID 1.2.840.113619.2.55.3.604688119.971.1687452301.412, AccessionNumber RIV-2024-889031, PatientID 40871, four series totaling 1,842 instances and about 1.1 GB, archived as JPEG2000 lossless. The site PACS pushes it into the VNA, where it lands as a single study object keyed on that UID plus a site qualifier of RIVERSIDE. Later, a radiologist opens it in a zero-footprint web viewer that cannot handle JPEG2000, so the Format Transcoder is invoked on demand to produce uncompressed Explicit VR Little Endian for series 3 (the 512-slice thin-cut arterial phase), carrying forward WindowCenter 40 / WindowWidth 400 and RescaleSlope/RescaleIntercept from the source. Nothing in the archive record changes; the transcoded bytes are what the viewer actually renders.
The dangerous part is that RIV-2024-889031 can be archived perfectly and still render wrong. If the second site, Lakeside, emits the same StudyInstanceUID after an interface restart — or emits it with the site qualifier missing — the later payload can overwrite Riverside's study under the same key, so radiologist A opens patient 40871 and sees Lakeside pixels. Equally, a burst reconnect after a network outage can replay series 1 and 2 while series 3 is still being written, and if transcoding is triggered before the write completes, the viewer gets a study that is silently missing its 512-slice arterial phase with no error surfaced. And a null or non-standard WindowWidth from one site's modality can map to a default during transcode, producing an image that is technically complete but diagnostically unreadable. Test this by archiving RIV-2024-889031, then deliberately pushing a Lakeside study carrying the identical UID and asserting the archive rejects or namespaces it rather than overwriting; separately, kill the connection mid-transfer after series 2, reconnect, and confirm a transcode request for that UID either blocks until all 1,842 instances are present or fails loudly rather than returning three series. For fidelity, transcode series 3 to uncompressed and compare pixel data hashes slice-by-slice against the source, and assert WindowCenter 40 / WindowWidth 400 survive intact — then repeat with WindowWidth explicitly nulled to confirm the transcoder surfaces the gap instead of substituting a default.
At CP2 the transcoded bytes for series 3 of RIV-2024-889031 stop being an archive concern and become a delivery concern: the Format Transcoder hands the Web Viewer API 512 uncompressed Explicit VR Little Endian slices for StudyInstanceUID 1.2.840.113619.2.55.3.604688119.971.1687452301.412 under site qualifier RIVERSIDE, each carrying WindowCenter 40 / WindowWidth 400 and the source RescaleSlope/RescaleIntercept, and the viewer acknowledges the delivery before painting anything on the radiologist's screen. The failure modes here are all of the "it said yes" variety. The viewer API's own validation can reject edge-case values that the archive happily tolerated — a RescaleIntercept of -1024 expressed as "-1024.0", or a null WindowWidth that survived transcode as a gap, bouncing the slice with a 400 rather than rendering it degraded. Uncompressed series 3 is several times the size of its JPEG2000 source, so pushing 512 slices at once can trip viewer-side throttling and return partial acceptance: slices 1–380 delivered, the rest 429'd and quietly dropped, and the radiologist scrolls to the end of the arterial phase and finds it just stops. Worst of all, the API can return 200 OK on the delivery for PatientID 40871 while nothing is actually persisted in the viewer's session cache, so a retry is never triggered and the next pan or zoom re-requests bytes that were never there.
Test it by delivering series 3 of RIV-2024-889031 to the Web Viewer API and asserting the response body enumerates 512 accepted SOPInstanceUIDs, not just an aggregate success — then immediately read back from the viewer's own retrieve path and count them again, because the read-back is the only proof the 200 meant anything. Re-send the same 512-slice payload with viewer-side rate limiting tightened until it throttles, and assert the transcoder either backs off and completes all 512 or fails the whole delivery for that UID rather than leaving 380 slices rendered as a complete arterial phase. For validation edges, replay the same series with RescaleIntercept formatted as "-1024.0" and separately with WindowWidth nulled, and confirm each is either accepted with the value intact or rejected with the offending tag named in the error — never silently normalized. Finally, assert the delivered study under site qualifier RIVERSIDE is retrievable only under that qualifier, so a same-UID Lakeside delivery cannot satisfy a read-back for RIV-2024-889031.
CP1 — Capture & Transform
Imaging Modality Fleet → Site PACS System → Vendor-Neutral Archive → Format Transcoder
CP2 — Target Delivery
Web Viewer API
Want this level of breakdown for your own system? Match your architecture in a few questions — no confidential upload required.