Troubleshooting

A practical guide to the failure modes you are most likely to hit. Each entry is symptom → likely cause → fix, with a link to the step page that covers it in depth. Most problems are either a step-ordering mistake (caught by Julia's dispatch as a MethodError) or a parameter mismatched to the data or optics.

MethodError from analyze (wrong step order)

Symptom. An error like MethodError: no method matching analyze(::Vector{...}, ::FilterConfig).

Cause. A step that consumes a BasicSMLD was placed before any DetectFitConfig, or the steps are otherwise in an impossible order. Only DetectFitConfig turns raw image data into an smld; every other step expects an existing smld, so dispatching one on a raw image stack finds no method.

Fix. Make DetectFitConfig the first step:

steps = [
    DetectFitConfig(boxer = BoxerConfig(boxsize = 9, psf_sigma = 0.130)),
    FilterConfig(photons = (500.0, Inf)),   # now receives a BasicSMLD
    RenderConfig(zoom = 20),
]

This is dispatch working as designed — a wrong order is a clear error, not a silent wrong answer. See The Pipeline Model.

Pipeline produced no SMLD

Symptom. Pipeline produced no SMLD. Did you include a DetectFitConfig step?

Cause. The steps vector contains no DetectFitConfig, so the orchestrator never produced any localizations to thread through the pipeline.

Fix. Add a DetectFitConfig as the first step (see Detection & Fitting). If you are resuming from a saved smld (smld = load_smld("after_detectfit.h5")), run the later steps directly on it rather than through a pipeline that starts from images.

Too many or too few detections

Symptom. The detection/fit overlay boxes noise instead of real spots, or misses obvious molecules; DetectFitInfo.n_fits is far below n_rois.

Cause. BoxerConfig.boxsize and psf_sigma are mismatched to your optics: a wrong psf_sigma (expected PSF width, in microns) mis-thresholds detection, and a boxsize too small for the PSF (or too large, merging neighbors) makes the fit reject candidates. A large n_roisn_fits gap is the tell.

Fix. Tune boxsize and psf_sigma to your pixel size and PSF, and choose a fitter PSF model that matches (GaussianXYNBS for free width, AstigmaticXYZNB for 3D); inspect the overlay figure after each adjustment. See Detection & Fitting.

Large inter-dataset shift warnings / bad drift

Symptom. A PROGRESS-level warning that inter-dataset shifts exceed ~500 nm, a jagged drift_trajectory.png, or a smeared reconstruction after drift correction.

Cause. The wrong DriftConfig.dataset_mode. Correcting one continuous movie as :registered (or independent overlapping acquisitions as :continuous) produces large spurious inter-dataset shifts.

Fix. Match the mode to how the data was acquired — :continuous for one long movie (e.g. DriftConfig(degree = 3, dataset_mode = :continuous, chunk_frames = 4000) for long runs), :registered for multiple overlapping acquisitions of the same field. See Drift Correction. Note that per-dataset frame numbering is by design (required by the Legendre normalization), not a bug.

Crowded data / suspiciously bright localizations

Symptom. A few localizations with abnormally high photon counts, or a reconstruction that looks denser/blurrier than expected.

Cause. When two fluorophores are active inside one diffraction-limited spot, the fit returns a single localization with a corrupted position and an inflated photon count. Separately, frame connection can over- or under-link in dense data.

Fix. Add an Intensity Filter (e.g. IntensityFilterConfig(cutoff = 0.01, field_mode = :gaussian)) to reject multi-emitter events with its Poisson upper-tail test; check its p2_estimate to gauge how crowded the data is. For Frame Connection, tune max_frame_gap: too large over-links distinct emitters into one track, too small fragments one emitter into many. A compression near 1 in the summary means almost nothing connected.

Out-of-memory on large acquisitions

Symptom. Julia runs out of memory while loading or detecting on a big acquisition.

Cause. Holding the entire image stack in memory at once.

Fix. Process the acquisition as multiple datasets — detection and fitting loop over datasets individually, so memory use stays bounded. Use file-based detection (a single file split into blocks, or one file per dataset) rather than passing all images in memory:

# MIC blocks auto-detected as datasets, loaded block by block
DetectFitConfig(path = "data.h5", h5_format = :mic,
                boxer = BoxerConfig(boxsize = 9))

See Multi-Dataset.

GPU / CUDA errors during fitting

Symptom. A CUDA-related error raised inside the Detection & Fitting step.

Cause. Fitting is GPU-accelerated through GaussMLE, which needs a working CUDA setup. The error surfaces when CUDA is unavailable or misconfigured.

Fix. Confirm your CUDA installation (driver, CUDA.jl functional); see Installation & Setup for requirements and the GaussMLE docs for its GPU/CPU options.

Empty output / no figures written

Symptom. The pipeline runs cleanly but no diagnostic figures or stats.md appear in the step output folders.

Cause. Figures are gated on two things: an outdir must be set, and verbose must be high enough. At Verbosity.SILENT/PROGRESS only counts and timing are emitted — basic figures (overlays, fit-quality, drift plots) start at Verbosity.STANDARD, diagnostics at DETAILED, movies at DEBUG.

Fix. Set an output directory and raise the verbosity:

config = AnalysisConfig(camera = cam, steps = [...],
                        outdir = "output/", verbose = Verbosity.DETAILED)

See Running a Pipeline for the verbosity levels and what each one writes.