{"record":{"id":"7d96ccb9f06f977e","repo":"unslothai/unsloth","slug":"the-requested-training-job-is-no-longer-active","errorCode":null,"errorMessage":"The requested training job is no longer active.","messagePattern":"The requested training job is no longer active\\.","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"warning","filePath":"studio/backend/routes/training.py","lineNumber":1768,"sourceCode":"):\n    \"\"\"\n    Stop the currently running training job.\n\n    Body:\n        save (bool): If True (default), save the model at the current checkpoint.\n        expected_job_id (str): Identifier of the job the caller intends to stop.\n    \"\"\"\n    try:\n        backend = get_training_backend()\n        outcome = await asyncio.to_thread(\n            _stop_training_if_active,\n            backend,\n            save = body.save,\n            expected_job_id = body.expected_job_id,\n        )\n        logger.info(\"Stop requested: save=%s outcome=%s\", body.save, outcome)\n        if outcome == \"superseded\":\n            raise HTTPException(\n                status_code = 409,\n                detail = \"The requested training job is no longer active.\",\n            )\n        if outcome == \"idle\":\n            return TrainingStopResponse(\n                status = \"idle\", message = \"No training job is currently running\"\n            )\n\n        return TrainingStopResponse(\n            status = \"stopped\",\n            message = \"Stop requested. Training will stop at the next safe step.\",\n        )\n\n    except HTTPException:\n        raise\n    except Exception as e:\n        raise log_and_http_error(\n            e,","sourceCodeStart":1750,"sourceCodeEnd":1786,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/training.py#L1750-L1786","documentation":"HTTP 409 from the stop endpoint when the stop operation returns outcome=='superseded': the job identified by expected_job_id is no longer the active job. The stop helper checks the caller's expected_job_id against the backend's current job before stopping, so a stale client cannot stop a newer run it did not see.","triggerScenarios":"POST /training/stop with expected_job_id set to a job that already finished, was reset, or was replaced by a newly spawned training job.","commonSituations":"A UI that holds an old job id after a page reload or after another tab started a new run; automation polling a finished job and then issuing stop with the stale id.","solutions":["Refetch the current training status to obtain the active job_id.","If no training is running, treat the stop as already satisfied — call stop without expected_job_id or handle the 'idle' response.","Only send expected_job_id when you genuinely intend 'stop exactly this run'."],"exampleFix":"// before\nawait post('/training/stop', {save: true, expected_job_id: staleId})\n// after\nconst status = await get('/training/status')\nif (status.job_id) {\n  await post('/training/stop', {save: true, expected_job_id: status.job_id})\n}","handlingStrategy":"validation","validationCode":"const status = await get('/training/status')\nconst target = status.job_id\nif (!target) { console.log('No active job to stop'); return }\nawait post('/training/stop', {save: true, expected_job_id: target})","typeGuard":null,"tryCatchPattern":"try { await post('/training/stop', body) } catch (e) { if (e.status === 409 && /no longer active/.test(e.detail)) { const s = await get('/training/status'); if (s.job_id) return post('/training/stop', {save: body.save, expected_job_id: s.job_id}) ; return } throw e }","preventionTips":["Always pair expected_job_id with a fresh status read in the same user action.","Treat 'superseded' stop 409s as benign — the job you meant to stop is already gone.","Handle the 'idle' response so normal no-op stops do not look like errors."],"tags":["stop","superseded","job-id","race-condition","http-409"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}