{"record":{"id":"8178b766c40b5c44","repo":"odysseus-dev/odysseus","slug":"stt-service-not-available-or-set-to-browser-mode","errorCode":null,"errorMessage":"STT service not available or set to browser mode","messagePattern":"STT service not available or set to browser mode","errorType":"http","errorClass":"HTTPException","httpStatus":503,"severity":"error","filePath":"routes/stt_routes.py","lineNumber":30,"sourceCode":"def setup_stt_routes(stt_service):\n    \"\"\"Setup STT routes with the provided STT service\"\"\"\n    router = APIRouter(prefix=\"/api/stt\", tags=[\"stt\"])\n\n    @router.get(\"/stats\")\n    async def get_stt_stats():\n        \"\"\"Get STT service statistics\"\"\"\n        try:\n            return stt_service.get_stats()\n        except Exception as e:\n            logger.error(f\"Failed to get STT stats: {e}\")\n            raise HTTPException(status_code=500, detail=str(e))\n\n    @router.post(\"/transcribe\")\n    async def transcribe_audio(file: UploadFile = File(...)):\n        \"\"\"Transcribe uploaded audio file to text\"\"\"\n        try:\n            if not stt_service.available:\n                raise HTTPException(\n                    status_code=503,\n                    detail={\"message\": \"STT service not available or set to browser mode\"}\n                )\n\n            audio_bytes = await read_upload_limited(file, STT_MAX_AUDIO_BYTES, \"Audio file\")\n            if not audio_bytes:\n                raise HTTPException(status_code=400, detail={\"message\": \"Empty audio file\"})\n\n            text = stt_service.transcribe(audio_bytes)\n            if text is None:\n                raise HTTPException(\n                    status_code=500,\n                    detail={\"message\": \"Transcription failed\"}\n                )\n\n            return {\"text\": text}\n\n        except HTTPException:","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/stt_routes.py#L12-L48","documentation":"503 from POST /api/stt/transcribe: stt_service.available is false, meaning the server has no server-side speech-to-text backend — either no provider is configured (no local Whisper, no API endpoint) or STT is deliberately set to browser mode, where transcription happens in the client and this endpoint is not the right path.","triggerScenarios":"POST /transcribe on a server whose STT config is 'browser'; a deployment where the Whisper model/dependency was never installed; the STT provider failed to initialize at startup (model load failure) leaving available=False.","commonSituations":"Default install with browser-based WebSpeech STT expected but the client calls the REST endpoint anyway; missing whisper/faster-whisper dependency or model download blocked; STT provider credentials absent.","solutions":["If browser mode is intended, use the browser STT path client-side and stop calling /transcribe.","To use server-side STT, configure a provider (local Whisper model or API endpoint) and restart so stt_service.available becomes true.","Check GET /api/stt/stats to see availability and provider state before sending audio.","Verify the STT dependency installed and the model downloaded (first-run downloads need network)."],"exampleFix":"# before\nr = client.post(\"/api/stt/transcribe\", files={\"file\": audio})  # 503\nr.raise_for_status()\n\n# after\nstats = client.get(\"/api/stt/stats\").json()\nif not stats.get(\"available\"):\n    transcript = browser_webspeech_transcribe(audio)  # or configure server STT\nelse:\n    r = client.post(\"/api/stt/transcribe\", files={\"file\": audio})\n    r.raise_for_status()\n    transcript = r.json()[\"text\"]","handlingStrategy":"type-guard","validationCode":"stats = client.get(f\"{base}/api/stt/stats\").json()\nif not stats.get(\"available\"):\n    raise ServiceUnavailable(\"server STT off/browser mode — configure a provider or use browser STT\")","typeGuard":"def stt_ready(stt_service) -> bool:\n    \"\"\"True when the server can transcribe uploads right now.\"\"\"\n    return bool(getattr(stt_service, \"available\", False))","tryCatchPattern":"r = client.post(f\"{base}/api/stt/transcribe\", files={\"file\": f})\nif r.status_code == 503:\n    transcript = browser_fallback_stt(audio_source)  # or prompt admin to configure server STT\nelse:\n    r.raise_for_status()\n    transcript = r.json()[\"text\"]","preventionTips":["Check /api/stt/stats before uploading audio.","Decide the STT mode once at app init: browser mode never calls /transcribe.","After install or upgrade, verify the Whisper model/dependency loads (available=True) before shipping to users."],"tags":["stt","http-503","configuration","speech-to-text","availability"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}