{"record":{"id":"6f103ea47be6009d","repo":"unslothai/unsloth","slug":"a-transformers-repair-is-replacing-the-latest-side","errorCode":null,"errorMessage":"A transformers repair is replacing the latest sidecar; retry when it completes.","messagePattern":"A transformers repair is replacing the latest sidecar; retry when it completes\\.","errorType":"exception","errorClass":"SidecarSwapInProgress","httpStatus":409,"severity":"warning","filePath":"studio/backend/core/inference/orchestrator.py","lineNumber":358,"sourceCode":"    # ------------------------------------------------------------------\n    # Subprocess lifecycle\n    # ------------------------------------------------------------------\n\n    def _spawn_subprocess(self, config: dict) -> None:\n        \"\"\"Spawn a new inference subprocess.\"\"\"\n        # Same recheck as the training/export spawns, REPAIR reservations only: a\n        # repair swaps without holding the lifecycle gate this load's caller owns,\n        # while an install cannot swap until this gate is released (and then its\n        # queued-load snapshot aborts it), so tolerating installs here lets the\n        # load win instead of failing both sides. Also covers the OpenAI\n        # auto-switch path, which enters _load_model_impl without route guards.\n        from utils.transformers_version import (\n            SidecarSwapInProgress,\n            sidecar_swap_kind,\n        )\n\n        if sidecar_swap_kind() == \"repair\":\n            raise SidecarSwapInProgress(\n                \"A transformers repair is replacing the latest sidecar; retry when it completes.\"\n            )\n        from utils.native_path_leases import (\n            native_path_secret_removed_for_child_start,\n            run_without_native_path_secret,\n        )\n        from utils.hf_cache_settings import child_environment_for_spawn, get_hf_cache_paths\n\n        cache_env = get_hf_cache_paths().child_env({})\n\n        with (\n            child_environment_for_spawn(cache_env),\n            native_path_secret_removed_for_child_start(),\n        ):\n            self._cmd_queue = _CTX.Queue()\n            self._resp_queue = _CTX.Queue()\n            self._cancel_event = _CTX.Event()\n            self._drain_event = _CTX.Event()","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/orchestrator.py#L340-L376","documentation":"Raised at model-load time when sidecar_swap_kind() reports 'repair': a transformers version repair is currently replacing the latest inference sidecar binary. Loads are aborted (not queued) during a repair because the repair swaps files without holding the lifecycle gate the loader owns; the caller should retry once the repair finishes.","triggerScenarios":"Calling the model-load path (_load_model_impl, including the OpenAI auto-switch route) exactly while a transformers version repair job is swapping the sidecar. The check deliberately tolerates installs (which queue) but not repairs.","commonSituations":"Model load request arriving right as an automated repair (e.g. after a corrupted/failed transformers install) starts; UI 'Load model' clicked during a background version fix; auto-switch to OpenAI racing a repair.","solutions":["Catch SidecarSwapInProgress and retry the load after the repair completes (poll sidecar_swap_kind() or subscribe to repair completion)","Surface 'repairing transformers runtime, retry shortly' in the UI instead of a hard failure","Avoid scheduling loads while repair jobs are known to run"],"exampleFix":"# before\nawait orchestrator.load_model(model_id)  # raises SidecarSwapInProgress once\n\n# after\nfor _ in range(30):\n    try:\n        await orchestrator.load_model(model_id)\n        break\n    except SidecarSwapInProgress:\n        await asyncio.sleep(2)  # repair still swapping; retry","handlingStrategy":"retry","validationCode":"from utils.transformers_version import sidecar_swap_kind\n\nif sidecar_swap_kind() == 'repair':\n    wait_for_sidecar_repair_completion()  # poll or subscribe before loading","typeGuard":null,"tryCatchPattern":"from utils.transformers_version import SidecarSwapInProgress\n\nfor _ in range(MAX_WAIT_SLOTS):\n    try:\n        await orchestrator.load_model(model_id)\n        break\n    except SidecarSwapInProgress:\n        await asyncio.sleep(2)","preventionTips":["Disable or queue load buttons while a repair job is known to run","Treat SidecarSwapInProgress as retryable by classification, not by string matching","Make repair completion observable so callers can await it instead of polling"],"tags":["orchestrator","sidecar","concurrency","retry","model-loading"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}