{"record":{"id":"c229dd530721813a","repo":"unslothai/unsloth","slug":"training-start-request-is-not-ready-to-acknowledge","errorCode":null,"errorMessage":"Training start request is not ready to acknowledge","messagePattern":"Training start request is not ready to acknowledge","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"warning","filePath":"studio/backend/routes/training.py","lineNumber":1093,"sourceCode":"        message = record.message,\n        error = record.error,\n        error_code = record.error_code,\n    )\n\n\n@router.post(\"/start-requests/{start_request_id}/acknowledge\")\nasync def acknowledge_training_start_request(\n    start_request_id: str = ApiPath(\n        ...,\n        min_length = 1,\n        max_length = 128,\n        pattern = TRAINING_REQUEST_ID_PATTERN,\n    ),\n    current_subject: str = Depends(get_current_subject),\n):\n    backend = get_training_backend()\n    if not backend.acknowledge_start_request(start_request_id):\n        raise HTTPException(\n            status_code = 409,\n            detail = \"Training start request is not ready to acknowledge\",\n        )\n    return {\"status\": \"ok\"}\n\n\n@router.post(\n    \"/start-requests/{start_request_id}/cancel\",\n    response_model = TrainingStartRequestStatus,\n)\nasync def cancel_training_start_request(\n    start_request_id: str = ApiPath(\n        ...,\n        min_length = 1,\n        max_length = 128,\n        pattern = TRAINING_REQUEST_ID_PATTERN,\n    ),\n    current_subject: str = Depends(get_current_subject),","sourceCodeStart":1075,"sourceCodeEnd":1111,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/training.py#L1075-L1111","documentation":"HTTP 409 from POST /training/start-requests/{id}/acknowledge when backend.acknowledge_start_request returns False, meaning the request is not in a state that accepts acknowledgement (not yet ready, already acknowledged, or terminal).","triggerScenarios":"Calling acknowledge before the start request reaches the ackable state; acknowledging twice; acknowledging a request that already succeeded, failed, or was cancelled.","commonSituations":"Client acknowledging immediately after submitting without polling for readiness; retry logic that re-acks after a timeout even though the first ack succeeded.","solutions":["Poll GET /training/start-requests/{id} until the state reported is ready for acknowledgement, then ack","Treat a repeated 409 on ack as 'already acknowledged' and verify via the status record's state field","If the request is terminal (completed/failed/cancelled), stop acking and branch on the terminal state"],"exampleFix":"// before\nclient.post(f\"/training/start-requests/{id}/acknowledge\")  # 409\n\n// after\nstatus = client.get(f\"/training/start-requests/{id}\").json()\nif status[\"state\"] == \"awaiting_acknowledgement\":\n    client.post(f\"/training/start-requests/{id}/acknowledge\")","handlingStrategy":"validation","validationCode":"def ready_to_acknowledge(state: str) -> bool:\n    return state == \"awaiting_acknowledgement\"  # align with backend state names","typeGuard":null,"tryCatchPattern":"resp = client.post(f\"/training/start-requests/{rid}/acknowledge\")\nif resp.status_code == 409:\n    state = client.get(f\"/training/start-requests/{rid}\").json()[\"state\"]\n    if state in (\"completed\", \"failed\", \"cancelled\", \"acknowledged\"):\n        pass  # nothing to ack; proceed\n    else:\n        retry_later()","preventionTips":["Poll the status endpoint and branch on state before acknowledging","Make acknowledge idempotent in your client: treat 409 after a prior success as done","Never ack blindly immediately after submit"],"tags":["training","http-409","acknowledge","state-machine"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}