{"record":{"id":"e97a46b1c2b9122c","repo":"unslothai/unsloth","slug":"the-dictation-server-is-not-running","errorCode":null,"errorMessage":"The dictation server is not running.","messagePattern":"The dictation server is not running\\.","errorType":"http","errorClass":"SttUnavailableError","httpStatus":501,"severity":"error","filePath":"studio/backend/core/inference/stt_mtmd_sidecar.py","lineNumber":997,"sourceCode":"        model_id = resolve_mtmd_model_id(model)\n        if cancel_event is not None and cancel_event.is_set():\n            raise SttTranscriptionCancelledError(\"Transcription cancelled.\")\n        # No training guard here on purpose: load() starts the server with\n        # -ngl 0 --no-mmproj-offload while a run is active, so this transcribes\n        # on CPU exactly as whisper.cpp and Transformers do. Refusing after a\n        # preload that succeeded only discarded the user's recording.\n        # Reject a missing model before decoding, matching the other sidecars.\n        self._ensure_model_downloaded(model_id)\n        decoded_audio = _decode_audio_bounded(audio, cancel_event)\n        if cancel_event is not None and cancel_event.is_set():\n            raise SttTranscriptionCancelledError(\"Transcription cancelled.\")\n        wav_bytes = _pcm_to_wav_bytes(decoded_audio)\n        audio_seconds = (len(decoded_audio) / _TARGET_SAMPLE_RATE) if len(decoded_audio) else None\n        self.load(model_id, request_cancel_event = cancel_event)\n        with self._lock:\n            port = self._port\n            if port is None or not self._process_alive():\n                raise SttUnavailableError(\"The dictation server is not running.\")\n            # Another client can switch models in the gap between that load\n            # returning and this lock, and the port read here would then be its\n            # server. Refuse rather than transcribe on the wrong model.\n            if self._model_id != model_id:\n                raise SttModelBusyError(\n                    \"The dictation model changed while this recording was being \"\n                    \"prepared. Try again.\"\n                )\n            # Long audio can outlast the keep-alive, and _post_transcribe runs\n            # outside the lock, so disarm the timer rather than let it kill\n            # llama-server mid-request and throw the dictation away.\n            self._active_requests += 1\n            self._cancel_idle_unload_locked()\n        try:\n            # Outside the lock: a held lock would block unload, including the\n            # one a training run performs, for the whole request timeout.\n            text = self._post_transcribe(\n                port, model_id, wav_bytes, audio_seconds, cancel_event = cancel_event","sourceCodeStart":979,"sourceCodeEnd":1015,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/stt_mtmd_sidecar.py#L979-L1015","documentation":"SttUnavailableError raised in transcribe_bytes() after load() returns: under _lock, self._port is None or _process_alive() is False. Something unloaded or killed the server in the gap between load() completing and this check — e.g. the idle-unload timer fired, another thread called unload(), or the process died on its own.","triggerScenarios":"load() succeeds, then before the transcribe critical section runs, the server is unloaded (idle timeout, training preempt, crash) leaving _port None or a dead process.","commonSituations":"Idle keep-alive expiry racing a slow decode; training run unloading sidecars during audio preparation; llama-server crashing under OOM right after startup.","solutions":["Retry the transcription — the retry's load() will bring a fresh server up.","If it recurs, check whether the idle-unload keep-alive is shorter than your decode time and raise it.","Inspect llama-server logs for a crash (OOM, bad file) if no unload was requested."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"```python\nfor attempt in range(2):\n    try:\n        return sidecar.transcribe_bytes(audio, cancel_event=ev)\n    except SttUnavailableError as exc:\n        if \"not running\" not in str(exc) or attempt:\n            raise\n        continue  # load() on retry brings a fresh server\n```","preventionTips":["Keep the idle-unload keep-alive longer than worst-case decode time.","Avoid calling unload() concurrently with in-flight transcriptions.","Watch llama-server stability (OOM) if this fires with no unload in sight."],"tags":["stt","server-lifecycle","race-condition","sidecar"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}