v0.4.1 release notes
v0.4.1 was released on October 9, 2026. It is v0.4.0 with one fix and nothing else: a compressed DICOM frame is now held to the image its header describes. Updating is recommended if you open files you did not create.
Compressed frames are held to their header (v0.4.1)
Section titled “Compressed frames are held to their header (v0.4.1)”- A compressed frame whose pixel data declares a different image than the file’s header is refused instead of being decoded at the size the pixel data declares. Before, a small file could make the viewer hold gigabytes of memory for one frame, and a JPEG frame larger than its header ended in an internal error. This affected v0.4.0 and earlier, for JPEG, JPEG-LS, JPEG 2000, and JPEG XL frames and for Deflated Image Frame Compression.
- Such a frame answers
500 pixel_decode_failedwith “pixel data disagrees with the header”. The file stays listed and its other frames display. Compared are rows, columns, samples per pixel, and the sample depth against Bits Allocated, and the frame’s tile, packet, and scan structure against fixed limits (4,096 tiles, 100 scans). - A JPEG 2000 frame stored as a JP2 file is decoded from its codestream alone: a palette or channel definition in the file’s other boxes is no longer applied.
- A JPEG XL frame with an alpha channel, floating-point samples, more bits than Bits Allocated, or an animation is refused. So is an 8-bit JPEG frame under a 16-bit header, which was displayed as noise.
- Files that differ from their header only in Bits Stored, sign, colour transform, resolution levels, or chroma subsampling display as before. No command-line option, Python parameter, or VS Code setting changed.
See troubleshooting when a file you rely on is refused.
The rest of this page describes v0.4.0, which v0.4.1 includes unchanged.
v0.4.0 was released on October 8, 2026. Every /api request now requires an access token, --unix-socket serves the viewer through a private Unix domain socket for shared servers, and --cache-budget sets the frame cache memory. Opening the viewer from the terminal, from Python view(), or in VS Code needs no change. Scripts that call the HTTP API directly must be updated.
Access token
Section titled “Access token”- Each
dcmviewprocess generates a token and requiresAuthorization: Bearer <token>on every request under/api, including/api/healthand the ROI export, over TCP and Unix sockets. A local user or a web page that can reach the loopback port can no longer read file lists, tags, or pixels. - The printed launch link carries the token in its fragment (
http://127.0.0.1:PORT/#token=…). The viewer takes it out of the address bar, keeps it for the tab across reloads, and sends it with every API request. - Opened without the token, or after
dcmviewrestarted on the same port, the page says to open the link printed in the terminal instead of showing a load error. Pasting that link into the same tab loads the viewer. DCMVIEW_TOKENfixes the token.--no-tokenturns the check off, with a warning, for a listener behind a proxy that already authenticates.- The warning for a non-loopback bind now says that plain HTTP does not encrypt the access token or DICOM data. It says the endpoints are unauthenticated only under
--no-token.
See ephemeral state and network security for the model and its limits.
Breaking change: scripts that call the HTTP API
Section titled “Breaking change: scripts that call the HTTP API”Scripts that call the API directly must send the token. Without it every /api path, unknown routes included, answers 401 with the error code unauthorized and WWW-Authenticate: Bearer. The token is read from the Authorization header only; a query parameter or cookie does not work.
dcmview --no-browser --port 8888 ./study &curl http://127.0.0.1:8888/api/filesexport DCMVIEW_TOKEN="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"dcmview --no-browser --port 8888 ./study &printf 'Authorization: Bearer %s\n' "$DCMVIEW_TOKEN" | curl --header @- http://127.0.0.1:8888/api/filesThe header is piped so that the token does not appear in curl’s arguments.
To use the generated token instead, read it from the startup line: dcmview --no-browser --startup-json ./study prints a server_started JSON line with base_url, token, and protocol. Append API paths to base_url, never after the #token=… fragment of url. In Python, non-blocking handles expose the same values as handle.base_url and handle.token; see the Python reference.
Other things to know when migrating:
- The printed URL and the startup
urlnow end in/#token=…. Treat that link and the startup JSON as credentials. DCMVIEW_TOKENmust be non-empty and use onlyA-Z a-z 0-9 - . _ ~. An invalid value, or the variable set together with--no-token, stops startup. The token is never read from the command line.--no-tokenrestores the open API of 0.3. Use it only behind something that authenticates.- A bookmarked
http://127.0.0.1:PORT/without the fragment shows the access-link page.
The API remains an unsupported internal protocol.
Private Unix socket
Section titled “Private Unix socket”- On Linux and macOS,
dcmview --unix-socket ~/dcmview/scan.sock ./studylistens on a socket only your account can reach, and prints thessh -L 8080:<socket> user@hostcommand and the link to open. - The directory must be yours and not writable by group or others; a missing one is created with mode
0700. The socket has mode0600, connections from another user ID are dropped, and a lock file beside the socket stops a second launch from replacing a running viewer’s socket. Both files are removed on shutdown. - Socket mode conflicts with
--hostand--port, always bypasses VS Code routing, and never opens a browser.
See Use a private Unix socket on a shared server.
Frame cache budget
Section titled “Frame cache budget”--cache-budget BYTES sets one total for the display, raw, and overlay frame caches, such as 256MiB. The minimum is 16MiB and the default remains 704 MiB. It limits cached frames, not total process memory. See the CLI reference.
Python
Section titled “Python”Non-blocking handles gain read-only token and base_url properties for authenticated API calls. handle.url is still the launch URL. Older binaries, and viewers routed into VS Code through the bridge, leave the new properties as None.
VS Code and Cursor
Section titled “VS Code and Cursor”- The extension forwards the viewer’s origin and attaches the token afterwards, so Remote-SSH port forwarding keeps working. It still works with older binaries.
- The output channel does not show the token.
--unix-socketis not supported indcmview.extraArgs: the extension needs an HTTP viewer URL and reports an unsupported launch.
Startup output
Section titled “Startup output”- The printed viewer URL (
dcmview: server running at ...) now ends in/#token=.... - The SSH forwarding hint is followed by a
then open http://localhost:PORT/#token=…line to copy on your local machine. - A failed browser launch no longer prints the opener’s error text, which could contain the launch URL. It says to open the launch URL manually.
- A redaction box change could leave a frame cached without the new box: a frame requested at the same instant as the change could be rendered with the earlier boxes and then served from the cache until the next change. This affected v0.3.2.
- Viewer shortcuts no longer fire while Ctrl, Cmd, or Alt is held, so Ctrl+Z no longer selects the Zoom tool and Ctrl+R no longer selects the ROI tool before the browser or VS Code handles the combination.
[and]still step frames when typed with Option or AltGr, and Delete and Backspace still remove the selected ROI with a modifier held. - Export ROIs shows a notice in the viewer when the export fails, instead of leaving the browser on an error page or doing nothing.
Documentation corrections
Section titled “Documentation corrections”- Earlier documentation described a right-drag zoom and wheel frame scrolling that the viewer does not have. Middle-drag pans, the wheel zooms about the pointer, and the wheel steps frames in the Scroll tool. See Adjust image presentation.
- The annotation CSV reference now states the checks that run when a CSV loads. Box bounds are checked when an edit is saved, not on load.
Internal protocol changes
Section titled “Internal protocol changes”Apart from the token requirement, no API endpoint or response field changed. The startup JSON only gains fields: base_url, token, protocol, and socket in socket mode. See the internal HTTP protocol.
Known limitations
Section titled “Known limitations”- The token does not encrypt anything. Over plain HTTP on a non-loopback bind, the token and the DICOM data are readable on the network. Keep the loopback default and forward with SSH.
- When
dcmviewopens the browser itself, the operating system’s opener receives the launch URL, token included, as a process argument that other local users can read on some systems. On a shared machine use--no-browserand open the printed link, or use--unix-socket. - A token fixed with
DCMVIEW_TOKENis not rate-limited; use a long random value. - The
--unix-socketdirectory needs a filesystem that supports file locks. Some network filesystems do not; use a local directory such as one under$XDG_RUNTIME_DIR. A forced kill can leave a stale socket, which the next launch on that path replaces. Forwarding a Unix socket needs OpenSSH 6.7 or newer on both ends. --unix-socketis not available on Windows, from Pythonview(), or in the VS Code extension.DCMVIEW_TOKENset only in a terminal or notebook is not passed through the VS Code bridge. A viewer the extension manages inherits the extension host’s environment and otherwise generates its own token, and a Python handle routed into VS Code hastokenandbase_urlset toNone.- The limits of v0.3.2 still apply: masking is a display aid, not de-identification. It does not change pixels, free text, or paths, and redaction boxes are not saved.
- The other known limitations still apply, including the uninterpreted VOI LUT Function and the limits of presentation state annotations.
Compatibility
Section titled “Compatibility”- The v0.4.0 Python wrapper and VS Code extension work with older binaries. An older Python wrapper works with a v0.4.0 binary, because the startup
urlit reads carries the token. - An older extension pointed at a v0.4.0 binary through
dcmview.binaryPathcan lose the token when VS Code forwards the port, as with Remote-SSH. Update the extension. - The CLI gains
--unix-socket,--no-token, and--cache-budget, and readsDCMVIEW_TOKEN. Existing CLI options and Python keywords are unchanged from v0.3.2. - v0.4.0 records no new broad compatibility campaign; see compatibility evidence.
See the documentation source for the exact release commit and media attribution for the release gallery.