From c049ac8a4127d089ef59a3869f8cba8821bd3661 Mon Sep 17 00:00:00 2001 From: serversdown Date: Sun, 21 Jun 2026 20:22:52 +0000 Subject: [PATCH] fix: SLM control no longer shows false "Unknown error" on start Starting a measurement could pop "Error: Unknown error" in the browser even though the device started recording fine. Two causes: the proxy's 10s timeout was shorter than a real device start over cellular, and on an httpx timeout str(e) is empty, so the relayed detail was "" -> the frontend's `result.detail || 'Unknown error'` rendered "Unknown error". - Raise the control proxy timeout to 30s so a healthy start isn't cut off. - Surface SLMM's own error detail on non-200 responses. - Add an explicit, honest timeout message. - Never return an empty detail (which rendered as "Unknown error"). Pairs with the SLMM-side fix that makes /start confirm promptly. Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/routers/slm_dashboard.py | 33 +++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/backend/routers/slm_dashboard.py b/backend/routers/slm_dashboard.py index d35746c..bb8d9a0 100644 --- a/backend/routers/slm_dashboard.py +++ b/backend/routers/slm_dashboard.py @@ -207,24 +207,39 @@ async def control_slm(unit_id: str, action: str): return {"status": "error", "detail": f"Invalid action. Must be one of: {valid_actions}"} try: - async with httpx.AsyncClient(timeout=10.0) as client: + # 30s: a real device start confirms over cellular in a few seconds, but + # leave headroom so a healthy start is never cut off mid-flight (which + # surfaced to users as a misleading "Unknown error"). + async with httpx.AsyncClient(timeout=30.0) as client: response = await client.post( f"{SLMM_BASE_URL}/api/nl43/{unit_id}/{action}" ) if response.status_code == 200: return response.json() - else: - return { - "status": "error", - "detail": f"SLMM returned status {response.status_code}" - } - except Exception as e: - logger.error(f"Failed to control {unit_id}: {e}") + + # Surface SLMM's own error detail when it provides one. + detail = f"SLMM returned status {response.status_code}" + try: + body = response.json() + if isinstance(body, dict) and body.get("detail"): + detail = body["detail"] + except Exception: + pass + return {"status": "error", "detail": detail} + except httpx.TimeoutException: + logger.error(f"Timeout controlling {unit_id} (action={action}) via SLMM") return { "status": "error", - "detail": str(e) + "detail": ( + f"Timed out waiting for the device to {action}. " + f"The command may still have been applied — refresh to confirm." + ), } + except Exception as e: + logger.error(f"Failed to control {unit_id}: {e}") + # Never return an empty detail — it renders to users as "Unknown error". + return {"status": "error", "detail": str(e) or f"{type(e).__name__}"} @router.get("/config/{unit_id}", response_class=HTMLResponse) async def get_slm_config(request: Request, unit_id: str, db: Session = Depends(get_db)):