{"record":{"id":"858b6280bb77c6d7","repo":"unslothai/unsloth","slug":"a-transformers-installation-is-in-progress-retry","errorCode":null,"errorMessage":"A transformers installation is in progress. Retry when it completes.","messagePattern":"A transformers installation is in progress\\. Retry when it completes\\.","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"warning","filePath":"studio/backend/routes/export.py","lineNumber":60,"sourceCode":"    ExportLoRAAdapterRequest,\n)\n\nrouter = APIRouter()\nlogger = get_logger(__name__)\n\n\nasync def _ensure_export_supported() -> None:\n    \"\"\"Reject a mutating export request up front (HTTP 400) when the host can't export.\n\n    Keeps the backend authoritative even if a client bypasses the UI gate. Read-only endpoints\n    (scan/status/logs) are intentionally NOT gated so the Export page can still render the reason.\n    Also refuses (409) while a latest-transformers install is swapping .venv_t5_latest: an\n    export worker spawned mid-swap could activate a half-replaced sidecar.\n    \"\"\"\n    from utils.transformers_latest import is_install_in_progress\n\n    if is_install_in_progress():\n        raise HTTPException(\n            status_code = 409,\n            detail = \"A transformers installation is in progress. Retry when it completes.\",\n        )\n\n    from utils.hardware import export_capability\n\n    # Off-loop: detection is deferred past bind, so the first call can wait on a cold import.\n    cap = await asyncio.to_thread(export_capability)\n    if not cap.get(\"export_supported\", True):\n        raise HTTPException(\n            status_code = 400,\n            detail = cap.get(\"export_unsupported_message\")\n            or \"Export is not supported on this platform.\",\n        )\n\n\n@router.post(\"/load-checkpoint\", response_model = ExportOperationResponse)\nasync def load_checkpoint(","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/export.py#L42-L78","documentation":"HTTP 409 raised by _ensure_export_supported (and every mutating export endpoint that depends on it) while a latest-transformers install is actively swapping the .venv_t5_latest sidecar. The guard exists because an export worker spawned mid-swap could activate a half-replaced virtual environment, corrupting the operation. It is by design retryable once the install finishes.","triggerScenarios":"Calling POST /export/load-checkpoint, /export/merged, /export/base, /export/gguf, or /cleanup while utils.transformers_latest.is_install_in_progress() returns True — i.e. a transformers upgrade was started from the UI or CLI and has not completed.","commonSituations":"User kicks off a transformers version upgrade, then immediately retries an export from another tab; an automated script that fires export requests without checking install status; long installs on slow networks colliding with scheduled exports.","solutions":["Wait for the transformers install to finish (watch the install status endpoint/UI) and retry the export.","On the client, treat 409 with this message as retryable with backoff rather than surfacing it as a hard failure.","Serialize operations: block export buttons in the UI while an install is in progress, mirroring the backend gate."],"exampleFix":"// before\nconst res = await api.post('/export/load-checkpoint', body);\n\n// after\nlet res;\nfor (let i = 0; i < 5; i++) {\n  res = await api.post('/export/load-checkpoint', body).catch(e => e);\n  if (!(res.status === 409 && /installation is in progress/.test(res.detail))) break;\n  await sleep(5000);\n}","handlingStrategy":"retry","validationCode":"const status = await api.get('/export/status'); // read-only, not gated\nif (status.install_in_progress) await waitForInstallCompletion();","typeGuard":null,"tryCatchPattern":"try {\n  await api.post('/export/load-checkpoint', body);\n} catch (e) {\n  if (e.status === 409 && /installation is in progress/.test(e.detail)) {\n    await sleep(backoffMs); return retry();\n  }\n  throw e;\n}","preventionTips":["Disable export actions in the UI while an install shows as in progress.","Treat 409 on mutating export endpoints as transient, never as a hard failure."],"tags":["concurrency","http-409","retryable","export","transformers"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}