{"record":{"id":"02f14ec90db7c368","repo":"unslothai/unsloth","slug":"validation-message","errorCode":null,"errorMessage":"{validation_message}","messagePattern":"\\{validation_message\\}","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"studio/backend/routes/training.py","lineNumber":1263,"sourceCode":"            )\n\n        resume_output_dir: Optional[str] = None\n        resume_run: Optional[dict] = None\n        resume_actual_model_repo_id: Optional[str] = None\n        resume_model_load_mode: Optional[str] = None\n        resume_requires_exact_resources = False\n        resume_requires_exact_model = False\n        resume_requires_exact_dataset = False\n        if request.resume_from_checkpoint:\n            try:\n                resume_output_dir = await asyncio.to_thread(\n                    normalize_resume_output_dir,\n                    request.resume_from_checkpoint,\n                )\n            except ValueError as e:\n                # Deliberate user-facing validation message.\n                validation_message = str(e)\n                raise HTTPException(status_code = 400, detail = validation_message)\n\n            resume_run = await asyncio.to_thread(\n                get_resumable_run_by_output_dir,\n                resume_output_dir,\n            )\n            if not resume_run or not await asyncio.to_thread(can_resume_run, resume_run):\n                detail = \"Resume checkpoint must belong to a stopped or errored run with complete saved trainer state.\"\n                # Only when the checkpoint itself is intact. can_resume_run refuses for several reasons and\n                # the blocker is computed independently of which one fired, so asking unconditionally would\n                # answer a provenance sentence even when the checkpoint is what is missing. has_resume_state\n                # is the discriminator can_resume_run itself short-circuits on.\n                if resume_run and await asyncio.to_thread(\n                    has_resume_state, resume_run.get(\"output_dir\")\n                ):\n                    from core.training.provenance import (\n                        resource_provenance_resume_blocker,\n                    )\n                    blocker = await asyncio.to_thread(","sourceCodeStart":1245,"sourceCodeEnd":1281,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/training.py#L1245-L1281","documentation":"HTTP 400 whose detail is the raw ValueError message from normalize_resume_output_dir: the resume_from_checkpoint value could not be normalized to a valid run output directory (bad path shape, non-run directory, unsafe characters, or similar path validation failure).","triggerScenarios":"POST /training/start with resume_from_checkpoint that is not a resolvable training output dir: relative junk path, a file instead of a directory, path with traversal, or a directory lacking run layout.","commonSituations":"Passing a checkpoint subdirectory instead of the run's output dir; typos; moving run directories so the stored path no longer resolves; copy/pasting a model dir rather than the trainer output dir.","solutions":["Read the returned validation_message - it states exactly why the path was rejected","Pass the run's top-level output directory (the one containing trainer state), not a checkpoint subfolder or model dir","Verify the path exists and was produced by a previous training run of this backend","Use absolute paths to avoid cwd-relative resolution surprises"],"exampleFix":"// before\nPOST /training/start {\"resume_from_checkpoint\": \"checkpoint-500\"}  // 400\n\n// after\nPOST /training/start {\"resume_from_checkpoint\": \"/data/runs/my-run/output\"}","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef valid_resume_output_dir(p: str) -> bool:\n    d = Path(p)\n    return d.is_absolute() and d.is_dir() and any(d.glob(\"checkpoint-*\"))","typeGuard":"def is_resume_output_dir(v: object) -> TypeGuard[str]:\n    return isinstance(v, str) and valid_resume_output_dir(v)","tryCatchPattern":"resp = client.post(\"/training/start\", payload)\nif resp.status_code == 400 and \"resume_from_checkpoint\" in payload:\n    show(resp.json()[\"detail\"])  # server's validation_message names the exact path problem","preventionTips":["Always pass the run's absolute output directory, never a checkpoint subfolder","Verify the directory exists and has run layout before sending","Surface the server's dynamic detail message to the user verbatim"],"tags":["training","resume","path-validation","http-400"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}