{"record":{"id":"ce670a437368adf5","repo":"unslothai/unsloth","slug":"save-directory-is-required","errorCode":null,"errorMessage":"save_directory is required","messagePattern":"save_directory is required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"studio/backend/models/export.py","lineNumber":15,"sourceCode":"# SPDX-License-Identifier: AGPL-3.0-only\n# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0\n\n\"\"\"Pydantic schemas for Export API.\"\"\"\n\nfrom pathlib import Path, PureWindowsPath\n\nfrom pydantic import BaseModel, Field, field_validator\nfrom typing import List, Optional, Literal, Dict, Any, Union\n\n\ndef _validate_save_directory(value: str) -> str:\n    \"\"\"Validate save_directory — allows absolute paths (user may want a different drive).\"\"\"\n    if value is None:\n        raise ValueError(\"save_directory is required\")\n    raw = str(value).strip()\n    if not raw:\n        raise ValueError(\"save_directory must not be empty\")\n    if \"\\x00\" in raw:\n        raise ValueError(\"save_directory may not contain null bytes\")\n    if any(ch in raw for ch in (\"\\r\", \"\\n\")):\n        raise ValueError(\"save_directory may not contain control characters\")\n    path = Path(raw).expanduser()\n    path_parts = (*path.parts, *PureWindowsPath(raw).parts, *raw.replace(\"\\\\\", \"/\").split(\"/\"))\n    if any(len(part) > 255 for part in path_parts if part not in (\"\", \".\", \"/\", \"\\\\\")):\n        raise ValueError(\"save_directory path components must be <= 255 characters\")\n    if (\n        \"..\" in path.parts\n        or \"..\" in PureWindowsPath(raw).parts\n        or \"..\" in raw.replace(\"\\\\\", \"/\").split(\"/\")\n    ):\n        raise ValueError(\"save_directory may not contain '..' segments\")\n    return raw","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/models/export.py#L1-L33","documentation":"ValueError from _validate_save_directory when the Export API request's save_directory field is None. The validator is the shared field_validator for all export request models; it fails fast on a missing destination before any filesystem work starts.","triggerScenarios":"POSTing an export request whose JSON omits 'save_directory' entirely or explicitly sets it to null, unless the model declares a default (some models mark it required, in which case Pydantic's own 'Field required' error fires first).","commonSituations":"Optional-field handling in clients that send null for unset values; schema drift where an older client omitted the field; copy/paste payloads missing the destination.","solutions":["Always include 'save_directory' as a string in export request payloads.","If the destination is user-chosen, default it in the client (e.g. last-used export dir) rather than sending null.","Treat the 422 detail message as the cue that the key is missing, not malformed."],"exampleFix":"// before\n{ \"format\": \"gguf\" }\n// after\n{ \"format\": \"gguf\", \"save_directory\": \"/exports/model\" }","handlingStrategy":"validation","validationCode":"def save_directory_present(payload: dict) -> bool:\n    return isinstance(payload.get(\"save_directory\"), str)","typeGuard":"def has_save_directory(p: dict) -> bool:\n    return isinstance(p.get(\"save_directory\"), str)","tryCatchPattern":null,"preventionTips":["Make save_directory a required field in client-side request types.","Never serialize unset optionals as null — omit the key or supply a real path.","Default the destination client-side to the user's last export directory."],"tags":["validation","export","api","http-422","filesystem"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}