{"record":{"id":"2b179fed2ff5575d","repo":"unslothai/unsloth","slug":"preset-name-is-reserved-or-empty","errorCode":null,"errorMessage":"Preset name is reserved or empty","messagePattern":"Preset name is reserved or empty","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"warning","filePath":"studio/backend/routes/settings.py","lineNumber":165,"sourceCode":"    height: int = Field(default = 512, ge = 32, le = 2048)\n    durationSeconds: float = Field(default = 3, gt = 0, le = 3600)\n    steps: int = Field(default = 8, ge = 1, le = 100)\n    guidance: float = Field(default = 1, ge = 0, le = 20)\n    flowShift: Optional[float] = Field(default = None, gt = 0, le = 100)\n    audioFlowShift: Optional[float] = Field(default = None, gt = 0, le = 100)\n\n\nclass MediaGenerationPreset(BaseModel):\n    model_config = ConfigDict(extra = \"forbid\")\n\n    name: str = Field(..., min_length = 1, max_length = 80)\n\n    @field_validator(\"name\")\n    @classmethod\n    def normalize_name(cls, value: str) -> str:\n        name = value.strip()\n        if not name or name == \"Default\":\n            raise ValueError(\"Preset name is reserved or empty\")\n        return name\n\n\nclass ImageGenerationPreset(MediaGenerationPreset):\n    params: ImageGenerationPresetParams\n\n\nclass VideoGenerationPreset(MediaGenerationPreset):\n    params: VideoGenerationPresetParams\n\n\nclass MediaGenerationPresetState(BaseModel):\n    \"\"\"A saved generation recipe and the selection that owns it.\n\n    Model-load options are deliberately not here: they take effect only on a reload, they follow\n    the hardware and the checkpoint rather than the recipe, and the resident build already reports\n    them, so a second stored copy would only ever compete with it.\n    \"\"\"","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/settings.py#L147-L183","documentation":"Pydantic validation failure (surfaces as HTTP 422 from FastAPI) raised by the field_validator on MediaGenerationPreset.name. The validator strips whitespace and rejects the result when it is empty or exactly the reserved name 'Default'. Pydantic wraps the ValueError into the 422 response body under the field path; it never reaches your handler code.","triggerScenarios":"PUT /settings/generation-presets/image|video/custom with name \"\" , \"   \", or \"Default\" / \"default \" — note only the exact case-sensitive 'Default' after strip is reserved, so 'DEFAULT' passes.","commonSituations":"Frontend form pre-filling 'Default' as a placeholder and submitting unchanged; user entering only spaces; copying a preset and forgetting to rename it; name submitted after a trim that left an empty string.","solutions":["Choose a non-empty, non-'Default' name (after trimming) and resubmit.","Add client-side validation mirroring the rule: strip, reject empty and exact 'Default', max 80 chars.","If you need a default preset, use the built-in one rather than saving a custom preset named 'Default'."],"exampleFix":"// before\nawait api.put('/settings/generation-presets/image/custom', { name: '  ', params }); // 422\n\n// after\nconst name = rawName.trim();\nif (!name || name === 'Default') throw new Error('Name must be non-empty and not \"Default\"');\nawait api.put('/settings/generation-presets/image/custom', { name, params });","handlingStrategy":"validation","validationCode":"function validPresetName(raw: string): boolean {\n  const name = raw.trim();\n  return name.length >= 1 && name.length <= 80 && name !== 'Default';\n}","typeGuard":"function isValidPresetName(v: unknown): v is string {\n  return typeof v === 'string' && v.trim().length >= 1 && v.trim().length <= 80 && v.trim() !== 'Default';\n}","tryCatchPattern":"try { await api.put(presetUrl, payload); }\ncatch (e) {\n  if (e.status === 422) { markNameInvalid(extractFieldError(e, 'name')); return; }\n  throw e;\n}","preventionTips":["Trim and validate the name field client-side with the same rule.","Use a distinct placeholder, never 'Default', in the rename form.","Reserve 'Default' in the UI as a non-editable built-in row."],"tags":["pydantic","validation","http-422","presets","fastapi"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}