Skip to content

Troubleshooting

Match the visible symptom, make the smallest change, and keep sensitive DICOM data, launch links, and access tokens out of public diagnostics.

Confirm that the package and interpreter agree:

Terminal window
python -m pip show dcmview-py
python -m dcmview_py --version

If the module works but dcmview does not, reopen the shell or add Python’s user scripts directory to PATH. Avoid installing repeated copies into different interpreters.

If the module cannot find a binary on an unsupported wheel platform, install a GitHub Release binary or source build and set DCMVIEW_BINARY to its absolute path. The wrapper requires a v0.2.0 or newer binary.

v0.4.1 source builds require Rust 1.88 or later, Node.js 20.19 or later, npm, CMake, and a C++ toolchain for the statically linked CharLS codec.

Terminal window
rustc --version
node --version
npm --version
cmake --version

The Cargo build compiles and embeds the Svelte frontend. DCMVIEW_SKIP_FRONTEND_BUILD=1 is only valid when frontend/dist/index.html already exists.

v0.3.0 removed --tunnel, --tunnel-host, --tunnel-port, and the matching Python keywords. Remove them from scripts, dcmview.extraArgs, and view() calls. Then run the ssh -L command that dcmview prints at startup on your local machine, as described in Inspect a study over SSH.

Check that the path exists and that the running user can read it. Directory scans recurse by default; remove --no-recursive if valid files live below the top level. Temporarily remove filters to distinguish unreadable input from readable-but-filtered objects. When every readable file was filtered out, the error names the active filters.

DICOMDIR is deliberately skipped because v0.4.1 does not parse the file-set hierarchy. Point the viewer at the directory containing ordinary DICOM instances.

The terminal’s completed-scan summary separates loaded, skipped, and filtered counts and breaks skipped files down by reason, such as files without a DICOM preamble or unparsable DICOM. Explorer shows the number indexed while a scan is still running.

To see which files were skipped and why, run with debug logging. Each skipped path and its reason is written to standard error:

Terminal window
RUST_LOG=dcmview=debug dcmview ./study

Skipped non-DICOM sidecar files are usually harmless. Filtered input is readable DICOM whose metadata did not satisfy every --filter predicate. Filter fields accept snake_case names or DICOM keywords; see the CLI reference.

The viewer opens before every file appears

Section titled “The viewer opens before every file appears”

This is expected during progressive discovery. Wait until Explorer’s indexing message disappears and the terminal prints the completed-scan summary. The whole directory tree is walked before the first file is inspected, so on a very large tree the first file appears only after that walk.

The idle clock starts only when the scan has finished, so a long scan never counts toward --timeout. After that, only API and browser requests reset the clock. A viewer left open without interaction still exits when the timeout passes; use a longer value or omit --timeout.

Omit --port or pass zero to let the operating system choose:

Terminal window
dcmview --port 0 ./study

If an SSH command depends on a fixed port, choose another value in both the viewer and forwarding command.

The server can still be healthy when the operating system browser launch fails. Use:

Terminal window
dcmview --no-browser ./study

Open the printed loopback URL manually, including its #token=… fragment. When the browser launch fails, dcmview says to open the launch URL manually and does not print the opener’s error text, which could contain the link. On a remote host, do not open the remote URL directly; follow the SSH forwarding guide.

Section titled “The page says “This viewer needs its access link””

The page was opened without the session’s access token, for example from a bookmark, a hand-typed http://127.0.0.1:<port>/, or a link copied without its fragment. Copy the full link, including #token=…, from the terminal where dcmview is running and paste it into the same tab.

After the viewer loads, the token is no longer in the address bar. Copying the address from a loaded tab therefore does not give a working link; copy it from the terminal.

The tab belongs to an earlier dcmview process that used the same address, so the token it holds is no longer valid. Each process generates a new token unless you fix it with DCMVIEW_TOKEN. Paste the new link from the terminal, including #token=…, into the same tab.

Every API path, including /api/health and unknown routes, needs the session’s bearer token. Without a valid one the server answers 401 with the code unauthorized and WWW-Authenticate: Bearer.

  • In a browser, open the complete launch link printed by the current process.
  • In a script, read base_url and token from the --startup-json line, append the API path to base_url, and send Authorization: Bearer <token>. In Python, use handle.base_url and handle.token.
  • Do not append an API path after the #token=… fragment of the launch URL.
  • A token in a query parameter or cookie does not authenticate.
  • A token from an earlier process does not work unless you fixed it with DCMVIEW_TOKEN.

The routes are an unsupported internal protocol.

Startup fails because of DCMVIEW_TOKEN or --no-token

Section titled “Startup fails because of DCMVIEW_TOKEN or --no-token”

DCMVIEW_TOKEN must be non-empty and contain only A-Z a-z 0-9 - . _ ~. An invalid value stops startup, and the error does not echo it. Setting the variable together with --no-token is also a startup error, even when the variable is empty; unset it before using --no-token.

--unix-socket reports the reason and exits. Match the message:

Message Fix
The directory is not owned by your effective user ID Use a directory that your account owns.
The directory is group- or other-writable Remove group and other write permission, or use another directory. A missing directory is created with mode 0700.
The directory cannot be created Only the socket’s own directory is created; create its parent first.
The path exists and is not a socket Choose another path. Regular files and symlinks are never replaced.
The socket is already in use Another viewer is running on that path. Stop it or choose another path.
Locking failed The filesystem does not support file locks, as on some network filesystems. Put the socket in a local directory such as one under $XDG_RUNTIME_DIR.
Unsupported platform Socket mode exists on Linux and macOS only. Use the loopback port with SSH forwarding.
A conflict with --host or --port Remove those options; socket mode does not bind a TCP port.

If ssh -L 8080:<socket> user@host fails, check that both ends run OpenSSH 6.7 or newer, which is required to forward a Unix socket. If the local port is busy, change 8080 in both the command and the link you open. A forced kill can leave a stale socket; the next launch on that path replaces it.

Socket mode is not available from Python view() or in the VS Code extension. The extension reports that Unix socket launches are not supported when --unix-socket is in dcmview.extraArgs.

The status bar says the server cannot be reached

Section titled “The status bar says the server cannot be reached”

The viewer reports a lost server in the status bar with a Retry action. Confirm that the dcmview process, and any SSH forwarding session, is still running, then choose Retry. If the same server answers, failed images and panels are requested again. If a new dcmview process now answers on the same address, its token differs, and the tab shows The viewer restarted: paste the new link from the terminal into the same tab. When both processes share a token fixed with DCMVIEW_TOKEN, or run with --no-token, the viewer instead reloads its file list from the new session rather than mixing data from both. A page left in the background checks the server when you return to it.

A frame reports that pixel data disagrees with the header

Section titled “A frame reports that pixel data disagrees with the header”

Since v0.4.1, a compressed frame (JPEG, JPEG-LS, JPEG 2000, JPEG XL, or Deflated Image Frame Compression) is decoded only when its own compressed data declares the image its DICOM header describes: the same rows, columns, and samples per pixel, and a sample depth that fits Bits Allocated. A frame that declares another image answers 500 pixel_decode_failed with “pixel data disagrees with the header”. The file stays listed and its other frames display.

The file is inconsistent, usually because its header was edited after compression or the pixel data was replaced. Re-encode it with a tool that writes the header from the compressed data. Files that differ from their header only in Bits Stored, sign, colour transform, resolution levels, or chroma subsampling display as before.

An image reports unsupported transfer syntax

Section titled “An image reports unsupported transfer syntax”

The object remains isolated and the viewer continues serving other files. Inspect its Transfer Syntax UID and compare it with v0.4.1 DICOM support.

Do not assume a browser can safely decode the original compressed fragment. dcmview requires a server-side display path and returns a controlled 422 error for unsupported syntax. A corrupt JPEG fragment is reported as a JPEG decode failure with the decoder’s reason rather than as an unsupported syntax.

The object may be metadata-only, may lack Pixel Data, or may have an unsupported geometry, numeric precision, component layout, or photometric interpretation. Check the file’s support state and reason in Explorer.

Metadata-only objects remain useful for tags and reference navigation. A no-pixel frame request returns 404; unsupported display layouts return a controlled 422 error, and decode failures are request-scoped. A file deleted or moved after discovery returns 404 naming its path.

A slide label or overview image is unsupported in a masked session

Section titled “A slide label or overview image is unsupported in a masked session”

This is expected. A session started with --mask does not show slide label and overview images, because they photograph the slide label with its identifiers. Explorer lists them as unsupported, and their frame requests return a controlled 403 error with the code masked. Tags remain available under the masking rules. To view the image, start the viewer again without --mask.

Identifiers are still visible in a masked session

Section titled “Identifiers are still visible in a masked session”

--mask is a display aid, not de-identification, and it does not cover everything on screen:

  • Text burned into the pixels stays visible. Cover it with the Redact tool (X).
  • Kept free-text values, such as Study and Series Description, can contain names or dates.
  • Explorer’s Directory view shows real folder and file names under a Not masked note, and tabs follow that view while it is showing. Switch Explorer to the Study and Series view.
  • The terminal, VS Code’s Explorer and custom-editor tabs, and the exported ROI CSV are outside the viewer page and keep real names and paths.

See Share your screen.

Redaction boxes live in server memory for one session. They are not saved, loaded, or included in Export ROIs, so draw them again after you restart the viewer.

The pixel readout says a value is unavailable

Section titled “The pixel readout says a value is unavailable”

The readout keeps row, column, and frame, but reports that the value is unavailable when the server cannot serve the frame’s raw samples, for example for an unsupported raw component layout. A real-world value appears only when the frame has a validated Real World Value Mapping or RT Dose Grid Scaling.

A real-world window falls back to the default window

Section titled “A real-world window falls back to the default window”

A window in the unit of a LUT mapping, or of a mapping behind a Modality LUT, is exact on the server only for 8-bit, 16-bit, and one-bit single-sample frames. Server-rendered frames with other samples, or whose preferred mapping has a different unit, are shown with their default window, and the HUD shows that window instead of the unit window. See DICOM and pixel support.

Confirm all three addresses:

  1. dcmview printed the expected remote loopback port.
  2. The SSH command forwards to that exact remote port.
  3. The browser opens the chosen local port with the printed #token=… fragment.

Run the ssh -L command on your local machine, not on the remote server. Keep the remote server bound to 127.0.0.1. If a local port is busy, change only the first -L port and the port in the local browser URL, and keep the fragment.

Behind a reverse proxy that serves the viewer under a path prefix, the proxy must strip the prefix before forwarding and serve the page with a trailing slash, such as .../8888/. The API still requires the token through the proxy unless dcmview was started with --no-token.

A launch opens in VS Code when it runs in a VS Code integrated terminal, or when its working directory is inside an open workspace folder. This applies to dcmview, dcmview-py, python -m dcmview_py, and dcmview_py.view(). Force one ordinary local launch:

Terminal window
export DCMVIEW_VSCODE_BYPASS=1
dcmview ./study

In Python, pass vscode_bridge=False. To stop terminal interception, disable dcmview.terminalInterception.enabled and create a new terminal.

Update the extension. The v0.3.0 binary no longer searches the bridge registry locations used by extension builds older than 0.2.5. Then confirm that the launch runs in an integrated terminal created after activation or inside an open workspace folder, and check dcmview: Show Bridge Status. Set DCMVIEW_VSCODE_BRIDGE_DEBUG=1 to print bridge discovery diagnostics to standard error.

Check dcmview: Show Bridge Status, then inspect dcmview.binaryPath. The extension otherwise tries the repository debug binary, its bundled platform binary, and PATH.

Verify that dcmview.extraArgs contains separate option/value elements, no removed --tunnel options, no --unix-socket, and no stale paths. Increase dcmview.startupTimeoutSeconds only when the process is healthy but slow to report its URL.

On an unsupported platform, supply an absolute compatible binary path.

Section titled “The VS Code viewer panel asks for its access link”

Update the extension. An extension older than v0.4.0 pointed at a v0.4.0 binary through dcmview.binaryPath can lose the token when VS Code forwards the port, as with Remote-SSH. The v0.4.0 extension forwards the viewer’s origin and attaches the token afterwards.

A DCMVIEW_TOKEN exported in a terminal is not forwarded to a viewer the extension manages, so do not expect that value to authenticate requests to it.

Confirm the required headers anon_dicom_path and ROI_coords. A JSON-valued CSV cell that contains a comma must be quoted. Then check:

  • [ymin, xmin, ymax, xmax] order with non-negative integer coordinates;
  • zero-based frame indices below the frame count;
  • matching counts for coordinates and frame lists; and
  • num_ROI, when present, matching the coordinate count.

Path matching uses normalized absolute identity. Start dcmview from the expected directory when the CSV uses relative paths.

An invalid matching row prevents the CSV transaction from committing, but image viewing continues. Annotation reads and export report the import error.

Loading does not check that a box lies inside the image or that ymax and xmax exceed ymin and xmin. Such a box loads and is exported as written, but the viewer refuses to save an edit to a file whose boxes break either rule; correct the CSV and start again.

The viewer reports a failed export in the page instead of leaving the browser on an error page. The usual cause is an annotation CSV whose import failed: fix the CSV as described above. If the server is no longer reachable, restore it and export again.

Use synthetic fixtures, redacted logs, and minimal reproduction steps. Debug logs list file paths, so redact them before sharing. Do not attach DICOM files, screenshots, annotation CSVs, launch links, access tokens, paths, patient/study identifiers, hostnames, or institutional metadata unless fully de-identified and approved for public sharing. Report suspected vulnerabilities privately.