mne-tools / mne-tools/mne-python

read_raw_eyelink() cannot handle empty first trial

Open
#13,550 8 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

BUG
Dominant language
Python
Stars
3.5k
Forks
1.6k
Avg merge
1d 6h
Merged PRs (30d)
100

Description

Description of the problem

In some experiments we start with a few practice trials that will be excluded from analysis, in some cases we put them outside the usual trial loop and has no calibration stage for them

read_raw_eyelink cannot handle these empty trials and will throw a ValueError because it cannot find eyetracking channels to parse annotations with

I know this looks pretty stupid and doesn't really look like a bug - but unexpected data format - though it'd be nice if read_raw_eyelink() can handle cases like these

Steps to reproduce
the problematic Eyelink files start with trials like these

START	1211913 	RIGHT	SAMPLES	EVENTS
PRESCALER	1
VPRESCALER	1
PUPIL	DIAMETER
EVENTS	GAZE	RIGHT	RATE	1000.00	TRACKING	CR	FILTER	1
SAMPLES	GAZE	RIGHT	RATE	1000.00	TRACKING	CR	FILTER	1	INPUT
INPUT	1211913	127
1211913	   .	   .	    0.0	  127.0	...
SSACC R  1211914
SBLINK R 1211914
1211914	   .	   .	    0.0	  127.0	...
1211915	   .	   .	    0.0	  127.0	...
1211916	   .	   .	    0.0	  127.0	...
MSG	1211917 TRIAL 1 STARTS
MSG	1211917 FIXATION START
MSG	1211917 pre 100 pause
1211917	   .	   .	    0.0	  127.0	...
1211918	   .	   .	    0.0	  127.0	...
1211919	   .	   .	    0.0	  127.0	...
1211920	   .	   .	    0.0	  127.0	...
1211921	   .	   .	    0.0	  127.0	...
1211922	   .	   .	    0.0	  127.0	...
1211923	   .	   .	    0.0	  127.0	...
1211924	   .	   .	    0.0	  127.0	...
1211925	   .	   .	    0.0	  127.0	...

calling read_raw_eyelink() will throw an error
Link to data

No response

Expected results

RawEyelink object with these trials converted to NaN values - or whatever value

Actual results

ValueError Traceback (most recent call last)
Cell In[15], line 10
8 pass
9 else:
---> 10 el_raw = read_eyelink_file(file=f)
11 s01e02_el_rawdict[el_parID] = (el_raw)

Cell In[13], line 24, in read_eyelink_file(file, raw, calib, screen_kwarg, event_id, interpolation_buffer, downsample)
22 file = Path(file) if isinstance(file, str) else file
23 assert isinstance(file, pathlib.Path), r'File needs to be string or pathlib.Path'
---> 24 raw = mne.io.read_raw_eyelink(file)
25 calib = mne.preprocessing.eyetracking.read_eyelink_calibration(file, **screen_kwarg)
26 elif (raw and calib) and not file:

File [~/AppData/Local/anaconda3/envs/mne/Lib/site-packages/mne/io/eyelink/eyelink.py#line=58), in read_raw_eyelink(fname, create_annotations, apply_offsets, find_overlaps, overlap_threshold, verbose)
29 """Reader for an Eyelink .asc file.
30
31 Parameters
(...) 55 'BAD_ACQ_SKIP'.
56 """
57 fname = _check_fname(fname, overwrite="read", must_exist=True, name="fname")
---> 59 raw_eyelink = RawEyelink(
60 fname,
61 create_annotations=create_annotations,
62 apply_offsets=apply_offsets,
63 find_overlaps=find_overlaps,
64 overlap_threshold=overlap_threshold,
65 verbose=verbose,
66 )
67 return raw_eyelink

File :12, in init(self, fname, create_annotations, apply_offsets, find_overlaps, overlap_threshold, verbose)

File [~/AppData/Local/anaconda3/envs/mne/Lib/site-packages/mne/io/eyelink/eyelink.py#line=126), in RawEyelink.init(self, fname, create_annotations, apply_offsets, find_overlaps, overlap_threshold, verbose)
123 eye_annots = _make_eyelink_annots(
124 self._raw_extras[0]["dfs"], create_annotations, apply_offsets
125 )
126 if gap_annots and eye_annots: # set both
--> 127 self.set_annotations(gap_annots + eye_annots)
128 elif gap_annots:
129 self.set_annotations(gap_annots)

File :12, in set_annotations(self, annotations, emit_warning, on_missing, verbose)

File [~/AppData/Local/anaconda3/envs/mne/Lib/site-packages/mne/io/base.py#line=746), in BaseRaw.set_annotations(self, annotations, emit_warning, on_missing, verbose)
745 delta = 1.0 / self.info["sfreq"]
746 new_annotations = annotations.copy()
--> 747 new_annotations._prune_ch_names(self.info, on_missing)
748 if annotations.orig_time is None:
749 new_annotations.crop(
750 0, self.times[-1] + delta, emit_warning=emit_warning
751 )

File [~/AppData/Local/anaconda3/envs/mne/Lib/site-packages/mne/annotations.py#line=653), in Annotations._prune_ch_names(self, info, on_missing)
652 if name not in keep:
653 if not warned:
--> 654 _on_missing(
655 on_missing,
656 "At least one channel name in "
657 f"annotations missing from info: {name}",
658 )
659 warned = True
660 else:

File [~/AppData/Local/anaconda3/envs/mne/Lib/site-packages/mne/utils/check.py#line=1220), in _on_missing(on_missing, msg, name, error_klass)
1219 on_missing = "warn" if on_missing == "warning" else on_missing
1220 if on_missing == "raise":
-> 1221 raise error_klass(msg)
1222 elif on_missing == "warn":
1223 warn(msg)

ValueError: At least one channel name in annotations missing from info: xpos_right

Additional information

Platform Windows-11-10.0.26100-SP0
Python 3.13.5 | packaged by conda-forge | (main, Jun 16 2025, 08:20:19) [MSC v.1943 64 bit (AMD64)]
Executable C:\Users...\AppData\Local\anaconda3\envs\mne\python.exe
CPU Intel(R) Core(TM) Ultra 5 135U (14 cores)
Memory 15.5 GiB

Core
X mne 1.10.1 (outdated, release 1.11.0 is available!) (sorry)

  • numpy 2.3.1 (MKL 2023.1-Product with 12 threads)
  • scipy 1.16.0
  • matplotlib 3.10.5 (backend=module://matplotlib_inline.backend_inline)

Numerical (optional)

  • sklearn 1.7.1
  • pandas 2.3.1
  • h5io 0.2.5
  • h5py 3.14.0
  • unavailable numba, nibabel, nilearn, dipy, openmeeg, cupy

Visualization (optional)

  • qtpy 2.4.1 (PyQt5=5.15.2)
  • pyqtgraph 0.13.7
  • mne-qt-browser 0.7.2
  • ipywidgets 8.1.8
  • unavailable pyvista, pyvistaqt, vtk, ipympl, trame_client, trame_server, trame_vtk, trame_vuetify

Ecosystem (optional)

  • mne-icalabel 0.7.0
  • unavailable mne-bids, mne-nirs, mne-features, mne-connectivity, mne-bids-pipeline, neo, eeglabio, edfio, mffpy, pybv

To update to the latest supported release version to get bugfixes and improvements, visit https://mne.tools/stable/install/updating.html

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the read_raw_eyelink entry point and the RawEyelink constructor shown in the traceback, then trace how annotations are built for files with an empty first trial. Reproduce the missing xpos_right channel error with the sample Eyelink data. Done means these trials load without ValueError and are represented as NaN values or another documented empty-trial representation.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
data
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.