Saving a multi-segment recording fails when only some segments have a t_start
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 84/100
Research direction
Start with the three constructors named in the issue: NumpyRecording and SharedMemoryRecording in src/spikeinterface/core/numpyextractors.py, and BinaryRecordingExtractor in src/spikeinterface/core/binaryrecordingextractor.py. Read how each handles get_segment_t_starts, then run the reproduction and the named recording and binary extractor tests. Done means binary and memory saves preserve the mixed start times and traces without leaked shared memory.
Written by the indexing model from the issue text.
Description
get_segment_t_starts returns a mixed list such as [None, 100.0] when only some segments have a start time. Three constructors then call float() on every entry. This happens with append_recordings([rec_without_t_start, rec_with_t_start]), and more generally when shift_times has been applied to only some segments.
Reproduction
main at 9ef464e66, Linux (WSL Ubuntu 24.04), Python 3.12.3, numpy 2.5.4, zarr 3.4.2:
import spikeinterface.core as sc
a = sc.generate_recording(num_channels=2, sampling_frequency=1000.0, durations=[3.0], seed=0)
b = sc.generate_recording(num_channels=2, sampling_frequency=1000.0, durations=[2.0], seed=1)
b.shift_times(100.0)
rec = sc.append_recordings([a, b])
print(rec.get_segment_t_starts(), [rec.get_start_time(i) for i in range(2)])
for kwargs in [
dict(format="binary", folder="rec_binary"),
dict(format="memory", shared_memory=False),
dict(format="memory", shared_memory=True),
dict(format="zarr", folder="rec.zarr"),
]:
try:
saved = rec.save(**kwargs)
print(kwargs["format"], "OK", [saved.get_start_time(i) for i in range(2)])
except TypeError as ex:
print(kwargs["format"], f"TypeError: {ex}")
[None, 100.0] [0.0, 100.0]
binary TypeError: float() argument must be a string or a real number, not 'NoneType'
memory TypeError: float() argument must be a string or a real number, not 'NoneType'
memory TypeError: float() argument must be a string or a real number, not 'NoneType'
zarr OK [0.0, array(100.)]
Three details make this worse:
- Binary: the exception comes after all traces are written. In the example,
rec_binarycontainstraces_cached_seg0.rawandtraces_cached_seg1.raw, but neitherbinary.jsonnorsi_folder.json. For a long recording, this means that hours of writing end in a folder that cannot be loaded. - Shared memory: the exception comes before
self.main_shm_owneris set, so__del__raisesAttributeError: 'SharedMemoryRecording' object has no attribute 'main_shm_owner', and the segments are not unlinked. The script ends withresource_tracker: There appear to be 4 leaked shared_memory objects to clean up at shutdown. - Zarr: saving works, but
get_start_time(1)returns a 0-darray(100.)instead of a float.
Cause
get_segment_t_starts only returns None when all segments have no start time. The conversion is in NumpyRecording, SharedMemoryRecording and BinaryRecordingExtractor. In SharedMemoryRecording, main_shm_owner is set after this line, and __del__ reads it.
Possible fix
In the three constructors, keep None:
t_starts = [None if t_start is None else float(t_start) for t_start in t_starts]
With this, the binary and both memory saves work, with start times [0.0, 100.0] and identical traces, and the script no longer reports leaked segments. test_basesorting.py, test_baserecording.py, test_binaryfolder.py, test_binaryrecordingextractor.py and test_numpy_extractors.py still pass (33 tests).
Two separate points would remain: setting main_shm_owner before anything that can fail, so that __del__ is safe after another error, and the 0-d array from zarr. I have not changed them.
I found no report of this case. Nearby: #2515 and #3120 (t_starts lost by save_to_memory) and #3940 (shift_times when t_start is None). I can open a PR with this change and a round-trip test.
- Dominant language
- Python
- Stars
- 870
- Forks
- 284
- Avg merge
- 3d 1h
- Merged PRs (30d)
- 41
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from SpikeInterface/spikeinterface
-
Difficulty 1/5 Under an hour Newbie friendliness 85/100
SpikeInterface/spikeinterface#4842 · 3 comments ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SpikeInterface/spikeinterface#4510 · 2 comments ·
Maintainers usually reply within 1 day
-
MountainSort5: expose `pca_seed` / `random_seed` sorter param (pair with MS5 configurable PCA seed)Open
Difficulty 4/5 3-5 days Newbie friendliness 43/100
SpikeInterface/spikeinterface#4919 ·
Maintainers usually reply within 1 day
-
BaseSorting.select_periods() drops properties, annotations and the registered recordingPossibly taken @AxelNoun claimed this today. Openbug core
Difficulty 2/5 1-3 hours Newbie friendliness 20/100
SpikeInterface/spikeinterface#4915 · 3 comments ·
Maintainers usually reply within 1 day
-
Optional dependencies don't participate in extension invalidationPossibly taken @alejoe91 claimed this today. Openbug core
Difficulty 4/5 3-5 days Newbie friendliness 25/100
SpikeInterface/spikeinterface#4912 ·
Maintainers usually reply within 1 day
All issues in SpikeInterface/spikeinterface
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
fritzprix/libr-agent#2612 ·
Maintainers usually reply within 1 day
-
needs-triage presubmit-gate
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
gke-labs/kube-agents#5001 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
simonw/sqlite-utils#928 ·
Maintainers usually reply within 3 days
-
colorado
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
freelawproject/juriscraper#2405 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
cline/mcp-marketplace#3576 ·