{"record":{"id":"07099e1ed71b1c23","repo":"unslothai/unsloth","slug":"server-lifecycle-changed","errorCode":null,"errorMessage":"server_lifecycle_changed","messagePattern":"server_lifecycle_changed","errorType":"exception","errorClass":"RuntimeError","httpStatus":409,"severity":"warning","filePath":"studio/backend/utils/remote_access_settings.py","lineNumber":265,"sourceCode":"        \"streaming_supported\": status[\"url\"] is None,\n    }\n\n\ndef start_remote_access(app_state) -> dict:\n    \"\"\"Schedule a settings-owned start. Repeated requests are idempotent.\"\"\"\n    global _start_worker, _start_worker_admission\n    from cloudflare_tunnel import (\n        capture_studio_tunnel_start_admission,\n        get_studio_tunnel_control_token,\n    )\n\n    admission = capture_studio_tunnel_start_admission()\n    if admission is None:\n        raise RuntimeError(\"server_shutting_down\")\n    status = remote_access_status(app_state)\n    current = get_studio_tunnel_control_token()\n    if current[0] != admission[0]:\n        raise RuntimeError(\"server_lifecycle_changed\")\n    if status[\"managed_by\"] == \"settings\" and status[\"state\"] in {\"starting\", \"online\"}:\n        return status\n    if not status[\"can_start\"]:\n        raise RuntimeError(status[\"block_reason\"] or \"operation_in_progress\")\n\n    port = getattr(app_state, \"remote_access_port\", None)\n    if not isinstance(port, int) or port <= 0:\n        raise RuntimeError(\"server_port_unavailable\")\n    if get_studio_tunnel_control_token() != admission:\n        raise RuntimeError(\"server_lifecycle_changed\")\n\n    def _start() -> None:\n        from cloudflare_tunnel import start_studio_tunnel\n        url = start_studio_tunnel(port, managed_by = \"settings\", admission = admission)\n        if url:\n            logger.info(\"Secure link access via Cloudflare: %s\", url)\n\n    _open_remote_access_stop_response_admission()","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/remote_access_settings.py#L247-L283","documentation":"Raised by start_remote_access() when the current studio tunnel control token no longer matches the admission captured at function entry. The tunnel controller rotates its token on lifecycle events (restart, new controller generation), so a mismatch means the world changed between capturing admission and acting on it. The call is aborted rather than acting on a stale generation.","triggerScenarios":"Calling start_remote_access() and, between capture_studio_tunnel_start_admission() and the token comparison, another lifecycle event rotates the control token — e.g. a concurrent stop/restart, controller regeneration, or shutdown racing the start request.","commonSituations":"Two settings requests racing (start and stop clicked in quick succession), a restart triggered while a start is in flight, or automation firing start during an update. Essentially a TOCTOU guard on tunnel generation.","solutions":["Re-read the current tunnel status and retry the start — the new generation will mint a fresh matching admission.","Serialize remote-access settings operations client-side (disable the Start/Stop buttons while one is in flight) to avoid racing requests.","If it recurs constantly, inspect logs for what keeps rotating the control token (crash loop, repeated restarts)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        return start_remote_access(app_state)\n    except RuntimeError as e:\n        if str(e) == 'server_lifecycle_changed' and attempt < 2:\n            time.sleep(0.5 * (attempt + 1))\n            continue\n        raise","preventionTips":["Serialize Start/Stop requests client-side so a token rotation cannot race your call.","On mismatch, always re-fetch status before retrying — never replay blindly in a tight loop."],"tags":["lifecycle","race-condition","tunnel","remote-access","transient","python"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}