You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Provenance. This issue was fundamentally diagnosed by Claude Code. I had independently run into this problem before I started using Claude Code, and I have reviewed and revised the entire report.
Exact prompt that drove the related-work research
write a DIPY issue for the kwargs-leak bug so I can copy and paste into their repo.
- Make sure you add: permanent links to code sections involved,
e.g., `https://github.com/dipy/dipy/blob/d9e9e30cedf89562b88cff120b0e69efb028d912/dipy/reconst/dti.py#L52-L54`
if something happened on those four lines.
- Be brief and to the point.
- Link the nifreeze issues originating the concern.
- Clearly write how you confirmed the bug exists.
BUT MOST IMPORTANT of ALL: **CHECK ALL OPEN AND CLOSED PRs and ISSUES**
to map out all the related work in the repo.
I then executed a follow-up prompt to extract the evironment information required in the issue template.
Summary
When a @multi_voxel_fit-decorated fit/multi_fit is called with a parallel engine (engine="joblib", "dask", or "ray"), the decorator forwards the orchestration kwargs (engine, n_jobs, vox_per_chunk, verbose) into the per-voxel fit function, which doesn't accept them. Every reconstruction model is affected; DKI is the concrete example below. This makes DIPY's own multi-voxel parallelization unusable from the public API.
multi_fit(engine="joblib"): TypeError: ls_fit_dki() got an unexpected keyword argument 'engine'
fit(engine="joblib"): TypeError: DiffusionKurtosisModel.fit() got an unexpected keyword argument 'engine'
(The second is secondary — in 1.10.0 DKIModel.fit has signature (data, *, mask=None) and doesn't forward engine; multi_fit is the decorated path and the one that hits the leak.)
Root cause
In multi_voxel_fit's parallel branch, the per-chunk kwargs are built from a full kwargs.copy() with the orchestration keys never removed, and _parallel_fit_worker forwards them straight into single_voxel_fit(...):
The batched-serial path on master already strips these keys (if k not in ("engine", "n_jobs", "vox_per_chunk", "verbose")), so the parallel branch just needs the same treatment — strip them from kwargs_chunks (or pop them in _parallel_fit_worker) before calling single_voxel_fit.
How I confirmed
Runtime: the repro above on dipy==1.10.0 (pip) yields the two TypeErrors.
Source: read the decorator on both 4eb161f (1.10.0) and d9e9e30 (master HEAD) — the orchestration kwargs are copied into per-voxel kwargs and never removed before dispatch in the parallel branch.
Platform
Linux 6.17.0-35-generic x86_64 GNU/Linux
DIPY version
1.10.0 (pip)
Environment
dipy==1.10.0
numpy==2.3.5
scipy==1.15.1
joblib==1.4.2
dask==2025.5.1
nibabel==5.3.2
threadpoolctl==3.5.0
tqdm==4.67.1
# ray: not installed — engine="ray" unavailable; bug reproduces with engine="joblib"/"dask"
NF: FORCE recon model #3844 (merged, 2026-03) — "NF: FORCE recon model" / multivoxel chunk integration + serialization rework = current master state; leak still present after it.
Summary
Warning
Provenance. This issue was fundamentally diagnosed by Claude Code. I had independently run into this problem before I started using Claude Code, and I have reviewed and revised the entire report.
Exact prompt that drove the related-work research
I then executed a follow-up prompt to extract the evironment information required in the issue template.
Summary
When a
@multi_voxel_fit-decoratedfit/multi_fitis called with a parallel engine (engine="joblib","dask", or"ray"), the decorator forwards the orchestration kwargs (engine,n_jobs,vox_per_chunk,verbose) into the per-voxel fit function, which doesn't accept them. Every reconstruction model is affected; DKI is the concrete example below. This makes DIPY's own multi-voxel parallelization unusable from the public API.Reproduce (DIPY 1.10.0, pip)
(The second is secondary — in 1.10.0
DKIModel.fithas signature(data, *, mask=None)and doesn't forwardengine;multi_fitis the decorated path and the one that hits the leak.)Root cause
In
multi_voxel_fit's parallel branch, the per-chunk kwargs are built from a fullkwargs.copy()with the orchestration keys never removed, and_parallel_fit_workerforwards them straight intosingle_voxel_fit(...):1.10.0 (
4eb161f)multi_voxel.py#L115-L125chunks kwargs built unstripped:dipy/dipy/reconst/multi_voxel.py
Lines 115 to 125 in 4eb161f
#L15-L35, worker forwards tosingle_voxel_fit:dipy/dipy/reconst/multi_voxel.py
Lines 15 to 35 in 4eb161f
master (
d9e9e30, after the recent rework)kw = kwargs.copy()unstripped at#L324-L344: same pattern persists:dipy/dipy/reconst/multi_voxel.py
Lines 324 to 344 in d9e9e30
#L101-L113, worker pops only_sobj_*/_batched/weights, not the orchestration keys, then forwards:dipy/dipy/reconst/multi_voxel.py
Lines 101 to 113 in d9e9e30
The batched-serial path on master already strips these keys (
if k not in ("engine", "n_jobs", "vox_per_chunk", "verbose")), so the parallel branch just needs the same treatment — strip them fromkwargs_chunks(or pop them in_parallel_fit_worker) before callingsingle_voxel_fit.How I confirmed
dipy==1.10.0(pip) yields the twoTypeErrors.4eb161f(1.10.0) andd9e9e30(master HEAD) — the orchestration kwargs are copied into per-voxel kwargs and never removed before dispatch in the parallel branch.Platform
Linux 6.17.0-35-generic x86_64 GNU/Linux
DIPY version
1.10.0(pip)Environment
Python version
3.12.8
Additional information
Related work in this repo
multi_voxel_fitdecorator (origin ofengine/paramap).quantize_evecs#3605 (merged) — parallelquantize_evecsviaparamap.multi_voxelparallel bug (single-CPUZeroDivisionError), already fixed.parallel_voxel_fitattempt.Origin
Surfaced while parallelizing DKI for head-motion estimation in NiFreeze: nipreps/nifreeze#442 (earlier attempt nipreps/nifreeze#142; interim fix nipreps/nifreeze#443, which works around it by avoiding the engine path).