Skip to content

Troubleshooting

Known startup and acquisition failures, what actually causes each one, and what the toolbox does about it today. These are real failures the project hit and fixed (or mitigated) — not a generic "things that could go wrong" list — so each entry says what you'll actually see.

Install and launch

conda env create fails: "Could not find 'share/conda-launchers/cli-64.exe'"

On Windows, with a fresh install of a recent Miniforge (seen with the 26.9.4 installer), conda env create -f environment.yml stops at Preparing transaction: failed with a FileNotFoundError naming share/conda-launchers/cli-64.exe. The environment is never created. The file is on disk; the installer wrote conda's own record of it incorrectly, so conda cannot find it. It is a conda/Miniforge defect (conda/conda#16856), and it affects any environment, not just this one. Upgrading conda afterwards does not repair the record.

Fix: rewrite the record once, from a terminal where conda works (the Miniforge Prompt), then create the environment again:

conda install -n base --force-reinstall conda-launchers
conda env create -f environment.yml

If an earlier attempt left an ssvep environment behind (the next conda env create refuses with "prefix already exists"), run conda env remove -n ssvep first.

The app asks "Set up this deployment?" on first launch

What you'll see: the very first time you open the app on a machine, a dialog appears — "This machine has no site profile…" — with three buttons: "Use generic (this machine has no lab/course)", "Set up a profile…", and "Not now".

This is not an error. It's a one-time question: this machine has no site profile (config/site.local.json) yet, and the app is asking once whether it should have one, rather than silently guessing. For a personal machine, a student laptop, or working through Getting started, click "Use generic…" — that's correct, and it writes a generic profile so the prompt won't reappear. For an actual lab or classroom deployment, "Set up a profile…" opens the full form (institution, push destination, approved operators, and the rest — see Site profile for what each field does).

Clicking "Not now" writes nothing, so the prompt comes back at the next launch rather than settling into some in-between state — if you keep seeing it, that's why; pick one of the other two buttons to make it stop.

The desktop icon shows a splash, then closes, with no error

What you'll see: double-clicking the Desktop shortcut briefly shows the splash screen, then the window disappears. No error dialog, no console — because the shortcut launches a console-less process (pythonw.exe), which cannot print anything at you even if it wanted to.

Where to look first:

%LOCALAPPDATA%\ssvep-toolbox\startup-error.log

The toolbox installs a crash handler at startup (sys.excepthook plus a thread-exception hook) that writes every unhandled exception here, with a header recording the launch environment (CONDA_PREFIX, whether the env's native library directory is on PATH, whether the install is editable). Read that first — it names the actual exception, not just "it closed".

If the log itself says something — a missing package, an import error, a schema/path problem — that is the real failure; fix whatever it names.

Reproduce with a console, to see the same failure interactively instead of reading it after the fact: run the console-script alias of the same app from a plain, non-activated shell (not one where you've run conda activate):

C:\path\to\envs\ssvep\Scripts\ssvep-design.exe

If that works but the icon doesn't, the difference is specific to the shortcut (see the next entry, and check the shortcut's working directory). If both fail, don't expect the console to show a traceback by itself — the next entry's failure mode terminates in native code with no Python exception, so a console alone doesn't reveal it; use the exit-code check there.

There is no log at all — not empty, absent

What you'll see: the same splash-then-close, but this time %LOCALAPPDATA%\ssvep-toolbox\startup-error.log — and even its parent ssvep-toolbox folder — does not exist. The crash-logging machinery above never ran.

What this means: the process died in native code, before any Python exception handler could run. Check the exit code to confirm:

& C:\path\to\envs\ssvep\Scripts\ssvep-design.exe; '0x{0:X8}' -f $LASTEXITCODE

0xC06D007F (ERROR_DELAY_LOAD_FAILED) is the signature of this failure. It is almost always MKL — numpy/scipy's compute backend — failing a delay-load: MKL loads its actual compute kernels lazily, at the first linear-algebra call, not at import numpy. That's why the app gets far enough to paint its splash (which only needs Qt) before vanishing the moment startup touches numpy. A structured exception like this terminates the process before Python's own exception handling ever runs, which is why the crash log stays empty rather than catching it.

Why it happens: the native libraries MKL needs live in the conda environment's Library\bin, and normally only conda activate puts that directory on PATH. A desktop shortcut launched by Explorer never runs conda activate — so a shortcut is exactly the situation where this bites, and a plain activated-shell launch usually is not.

What the toolbox does about it: import ssvep now repairs PATH in-process, before any numeric code runs, so this should not happen regardless of how the app was launched. If you still see it: confirm the environment actually has a Library\bin directory (a broken or partial env install can be missing it), and confirm the shortcut is pointed at the environment you think it is — a stale shortcut from before a conda env remove/recreate will point at a path that no longer has what it needs.

Install/launch fails, or crashes on first real computation, after pip install

If you ran pip install -e . without --no-deps (or added packages with plain pip install afterward), pip may have re-resolved and overwritten some of the conda-built dependencies with PyPI builds of the same package — most consequentially numpy's BLAS backend. The import can still succeed; the crash shows up the moment something does real linear algebra, which can look identical to the MKL delay-load failure above but for a different underlying reason. Fix: deactivate first (conda deactivate — conda refuses to remove the environment you're currently in), then recreate it (conda env remove -n ssvep, then conda env create -f environment.yml), reactivate (conda activate ssvep), and reinstall with pip install -e . --no-deps. See Installation for why the flag matters.

pytest (or the app) imports ssvep from a different checkout than you expect

pip install -e . records an absolute path to the checkout it was run in, and the environment always imports ssvep from whichever checkout ran that command most recently — reinstalling from a second clone does update it, no need to uninstall the first. The confusing case is the opposite: you move, rename, or delete the checkout that install still points to (or switch to working in an older second clone) without re-running pip install -e . --no-deps from the checkout you actually mean to use — the environment keeps importing from the old path regardless of which folder your terminal is sitting in. If behaviour doesn't match the code you're looking at, first confirm which tree is really being imported (python -c "import ssvep; print(ssvep.__file__)" from the activated env), and reinstall from the checkout you intend to use if it's wrong.

Acquisition

After finishing the impedance check, starting the run is refused for several seconds

What you'll see: you run the impedance/QA check, click Done, prepare the run (Prep selected run), then click Start recording on the live-EEG pane — and for a few seconds that's disabled, or refuses, reporting that impedance is still running, even though you already stopped it.

What this means: this is expected on real hardware, not a bug to work around. Releasing the amplifier from the impedance-measurement (lead-off) mode is a real, multi-step handoff — on the OpenBCI Cyton it's several sequential serial round-trips plus a session release, which has been observed to take five to ten seconds. The check now reports that state honestly: the status line reads "stopping… (releasing the amplifier — this can take several seconds)", and the run genuinely cannot start until the amplifier has actually finished releasing — showing "ready" earlier than that would be reporting a state that isn't true yet.

What to do: wait. It resolves itself once the amplifier finishes releasing; there is nothing to retry or reconfigure. This does not occur on the synthetic test board — there's no real amplifier to hand off, so nothing to measure or release (see Getting started).

Stimulus display

A run is refused: "swap interval is 0" or "measured … faster"

What you'll see: a fullscreen run stops before its first trial, and the Run tab (or the renderer's log) shows REFUSED: the … stimulus window's swap interval is 0 or REFUSED: the … stimulus window measured 245.00 Hz against a 240 Hz frame clock (…% faster…).

What this means: the stimulus window is not locked to the panel's refresh. With the swap interval at 0, vsync is off and frames tear. Measured more than 1 % faster than the display's mode, frames are being drawn faster than the panel shows them, so some never reach the screen: a swap that waits for the panel's vertical blank cannot do that. Either way the run would not present what it records, so it does not start.

What to do (NVIDIA; other drivers have the same settings under other names):

  • NVIDIA Control Panel → Manage 3D settings → Vertical sync: "Use the 3D application setting" (or On). Never Off, Fast or Adaptive. Set it globally, or in a program profile for the python.exe of the ssvep environment.
  • G-Sync / variable refresh off for the stimulus program (Set up G-SYNC, or the program profile's Monitor technology: Fixed refresh). A variable-refresh panel stretches individual frames to match the program instead of refreshing at its mode.
  • Use the panel as configured: run it at its fixed mode rate (Windows Dynamic refresh rate off), at the resolution and refresh the design was built for, and do not cap the frame rate in the driver.
  • Then start the run again.

The Windows 11 caveat. The stimulus window is a borderless window at the desktop's mode, not exclusive fullscreen. Windows 11 may compose it through the desktop window manager for the first seconds of a run and then promote it to independent flip, and is more likely to keep composing it when the stimulus panel is not the primary display or shares the desktop with a monitor at a different refresh rate. Make the stimulus panel the primary display (or the only one active), keep every other screen still during a run, and do not touch the stimulus PC in the run's first ~20 s. Every flip is followed by glFinish() and the flicker follows the refresh clock, so a composited frame that misses a refresh no longer shifts the waveform; the refusal catches the case where frames get ahead of the panel, which nothing after the fact can repair. To see which presentation mode a run actually got, run Intel's PresentMon (a separate, free tool) alongside it: Hardware: Independent Flip is what you want; Composed: Flip or Composed: Copy means the compositor is in the path.

The run's own record: the renderer's log ends with glFinish after each flip: N frames, blocked … ms on average, and the sidecar's provenance.presentation_timing.display_sync carries the swap interval and that cost.

If none of this matches what you're seeing

The startup-error log (above) is the first thing to capture — most launch failures leave a trace there even when the on-screen symptom looks identical to something else. Beyond that, this project's issue tracker is where failures like these get diagnosed and fixed — every entry above started as a bug report there, filed with exactly this kind of detail: what you did, what you expected, and what you saw instead.