unslothai/unsloth · error · SttModelBusyError
The previous dictation worker did not exit and still holds i
Error message
The previous dictation worker did not exit and still holds its memory. Try again shortly.
What it means
Raised in load() when _release_engine_locked() fails to retire the previous dictation worker child process before starting a new one. The comment explains why: starting a second child over one that never exited would double GPU/CPU memory, so the load refuses and a timer retries the release.
Source
Thrown at studio/backend/core/inference/stt_sidecar.py:1485
candidate = None
device: Optional[str] = None
resident_released = False
self._start_survivor = None
try:
self._raise_if_load_cancelled(cancel_event)
cached = self._ensure_model_downloaded(model_id)
snapshot_path = cached.path
if snapshot_path is None:
raise SttModelNotDownloadedError(
f"STT model '{model_id}' is not downloaded. "
"Download it in Settings, then Voice, before loading it."
)
self._raise_if_load_cancelled(cancel_event)
device, dtype = _pick_device()
if not self._release_engine_locked():
# Starting a second child over one that never exited doubles
# the memory this release was meant to give back.
raise SttModelBusyError(
"The previous dictation worker did not exit and still holds its "
"memory. Try again shortly."
)
resident_released = True
logger.info("Loading STT model %s (%s) on %s", model_id, snapshot_path, device)
def not_downloaded(cause: BaseException) -> SttModelNotDownloadedError:
return SttModelNotDownloadedError(
f"STT model '{model_id}' is not downloaded. "
"Download it in Settings, then Voice, before loading it."
)
retry_on_cpu = False
try:
candidate = self._build_model(str(snapshot_path), device, dtype, cancel_event)
self._raise_if_load_cancelled(cancel_event)
except SttLoadCancelledError:
raiseView on GitHub (pinned to 203007d190)
Solutions
- Simply retry after a short wait — the release timer keeps trying and the next load usually succeeds.
- If persistent, inspect for orphaned worker processes (ps | grep the sidecar worker) and kill them manually.
- In containers, ensure the init/reaper (e.g. tini) is set so killed children are reaped; check GPU memory with nvidia-smi for a leaked worker.
Example fix
# before
engine = sidecar.load(new_model) # previous worker alive -> SttModelBusyError
# after
for _ in range(5):
try:
engine = sidecar.load(new_model); break
except SttModelBusyError:
time.sleep(2) Defensive patterns
Strategy: retry
Validate before calling
import subprocess
def leaked_workers() -> list[str]:
out = subprocess.run(["ps", "-eo", "pid,cmd"], capture_output=True, text=True).stdout
return [l for l in out.splitlines() if "stt" in l and "worker" in l] Try / catch
for _ in range(5):
try:
engine = sidecar.load(model); break
except SttModelBusyError:
time.sleep(2) # release timer retries meanwhile
else:
kill_leaked_workers(); engine = sidecar.load(model) Prevention
- Run containers with an init/reaper so killed workers are reaped
- Avoid rapid model switching; let one switch settle first
- Monitor GPU memory for leaked worker processes after failures
When it happens
Trigger: Switching models (or reloading after eviction) while the previous worker subprocess has not terminated — it survived its kill signal, is hung in a CUDA call, or is a zombie the reaper has not reaped.
Common situations: Worker stuck in a long GPU kernel or driver teardown; SIGKILL not yet delivered/processed under load; PID namespace or zombie-reaping issues in containers; rapid model switching thrash.
Related errors
- A transcription is still running on the current dictation mo
- llama-server did not become ready for '{model_id}'.
- STT model loading was cancelled so training could start.
- STT model loading was cancelled so training could start.
- Transcription cancelled.
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/f893d25ad7999321.
Report an issue: GitHub.