Debugging playbook#

This page is a step-by-step debugging runbook for Cellucid Python developers and power users.

It is optimized for two outcomes:

  1. you can fix the issue yourself, or

  2. you can file a bug report that a maintainer can act on without guessing.


Step 0 — Identify which mode you are in#

Everything downstream depends on this.

Pick the closest match:

Mode A: Export folder#

  • you ran cellucid.prepare(...) and have a directory export.

  • you start the viewer with cellucid serve ./export_dir or cellucid.show("./export_dir").

Mode B: AnnData server#

  • you are serving .h5ad/.zarr or an in-memory AnnData.

  • you start the viewer with cellucid serve ./data.h5ad --dataset-name "My dataset" --dataset-id my-dataset or cellucid.show_anndata(...).

Mode C: Notebook embedding#

  • you are in Jupyter/VSCode/Colab.

  • you see an iframe (or a blank output where you expected an iframe).


Step 1 — Confirm the server is alive#

Open a terminal (or use curl from wherever the server is running) and check:

curl -s http://127.0.0.1:8765/_cellucid/health
curl -s http://127.0.0.1:8765/_cellucid/info

If these fail:

  • your server is not running, crashed, or the port is wrong.

If the port differs, find it in:

  • the CLI banner output, or

  • viewer.server_url (notebook), or

  • server.url (Python).


Step 2 — Confirm the viewer UI assets can load#

Start the viewer/server. Source-generation errors occur before the HTTP server binds and identify the failing inventory, object, verification, or filesystem operation.

Fix options:

  1. ensure access to the configured web_source_url,

  2. correct the exact source response or inventory contract, and

  3. pass a writable web_cache_dir.

See: Server mode architecture, endpoints, and security and Configuration, environment variables, and logging.


Step 3 — Confirm dataset endpoints return data#

Export folder mode#

Check that the directory contains:

  • dataset_identity.json

  • obs_manifest.json

  • at least one points_*d.bin or points_*d.bin.gz

Then probe:

curl -I http://127.0.0.1:<port>/dataset_identity.json
curl -I http://127.0.0.1:<port>/obs_manifest.json
curl -I http://127.0.0.1:<port>/points_3d.bin
curl -I http://127.0.0.1:<port>/points_3d.bin.gz

(One of the two points paths should exist depending on compression.)

AnnData server mode#

Probe:

curl -I http://127.0.0.1:<port>/dataset_identity.json
curl -I http://127.0.0.1:<port>/obs_manifest.json
curl -I http://127.0.0.1:<port>/points_3d.bin

If you want to test gene/obs endpoints, pick a known key and try:

curl -I http://127.0.0.1:<port>/var/FOXP1.values.f32
curl -I http://127.0.0.1:<port>/obs/cell_type.codes.u8

Step 4 — Use browser DevTools (network + console)#

This is the fastest way to distinguish:

  • server failures (404/500),

  • format/schema issues (manifest parse errors),

  • and frontend/UI issues.

4.1 Network tab checklist#

In browser DevTools → Network:

  1. filter for _cellucid:

    • /_cellucid/health should be 200

    • /_cellucid/info should be 200

  2. filter for obs_manifest.json and var_manifest.json:

    • should be 200 and have JSON content

  3. look for 404s:

    • missing .gz files often means a partial export / compression mismatch

  4. look for CORS errors:

    • “CORS blocked” usually means origin mismatch or a proxy issue

4.2 Console tab checklist#

In DevTools → Console:

  • copy/paste the first relevant error (don’t paraphrase)

  • look for:

    • “Failed to fetch”

    • “CORS blocked”

    • JSON parse errors (manifest schema mismatch)

If you suspect web-app internal bugs, also consult:


Step 5 — Notebook-specific debugging (viewer.debug_connection())#

If you have a viewer object (Jupyter/VSCode/Colab), run:

report = viewer.debug_connection()
report

The report includes:

  • server health/info probes,

  • dataset_identity_probes, keyed by every dataset declared by the server, preserving each exact dataset id/path and recording failures per dataset,

  • whether the viewer is displayed,

  • web UI cache status + build id,

  • a Python→frontend ping/pong roundtrip,

  • a frontend “debug snapshot” (URL/origin/userAgent as seen by the iframe),

  • and recent accepted-event counts grouped by exact event type.

If you are filing a bug report, paste the report as JSON:

import json
print(json.dumps(report, indent=2, default=str))

Common failure patterns (fast diagnosis)#

Pattern: “Viewer UI loads, but data is missing”#

Export folder mode:

  • manifests might reference files that don’t exist (partial export)

  • exact identifier/path mismatch

  • .gz mismatch (exported compressed, but viewer requests uncompressed or vice versa)

AnnData server mode:

  • route for that path may not exist (wrong filename/ext)

  • adapter could be raising internally (check server logs)

Start with:

Pattern: “Hooks don’t fire”#

Confirm in browser Network tab:

  • POST /_cellucid/events is happening and returns 200.

If it is not happening:

  • frontend may not be configured for notebook mode,

  • iframe may be blocked by proxy/mixed-content issues.

If it is happening:

  • viewerId may mismatch (stale iframe vs new viewer),

  • Python viewer may have been stopped/garbage-collected.

Start with:

Pattern: “Everything works locally, fails on a remote server”#

Likely causes:

  • binding host incorrectly (127.0.0.1 vs 0.0.0.0)

  • missing SSH tunnel

  • corporate firewall

  • HTTPS notebook mixed-content

Fix:

  • prefer SSH tunneling over public binding,

  • or provide a stable reverse proxy and pass its exact base as client_server_url=.


What to include in a high-quality bug report#

Minimum:

  • mode (export folder vs AnnData vs notebook)

  • cellucid version + Python version + OS

  • reproduction steps (code or click-by-click)

  • expected vs actual

  • server URL + what /_cellucid/health returns

  • browser console error(s)

Notebook:

  • attach viewer.debug_connection() report.