{"record":{"id":"48f4e0f009e524f5","repo":"unslothai/unsloth","slug":"invalid-preset-name","errorCode":null,"errorMessage":"Invalid preset name","messagePattern":"Invalid preset name","errorType":"http","errorClass":"HTTPException","httpStatus":422,"severity":"warning","filePath":"studio/backend/routes/settings.py","lineNumber":456,"sourceCode":"    return _upsert_custom_generation_preset(\"image\", payload)\n\n\n@router.put(\"/generation-presets/video/custom\")\ndef upsert_custom_video_generation_preset(\n    payload: VideoGenerationPreset, current_subject: str = Depends(get_current_subject)\n) -> dict[str, bool]:\n    return _upsert_custom_generation_preset(\"video\", payload)\n\n\n@router.delete(\"/generation-presets/{kind}/custom\")\ndef delete_custom_generation_preset(\n    kind: Literal[\"image\", \"video\"],\n    name: str,\n    current_subject: str = Depends(get_current_subject),\n) -> dict[str, bool]:\n    name = name.strip()\n    if not name or name == \"Default\" or len(name) > 80:\n        raise HTTPException(status_code = 422, detail = \"Invalid preset name\")\n    delete_media_generation_preset(kind, name)\n    return {\"deleted\": True}\n\n\nclass UploadLimitPayload(BaseModel):\n    max_upload_size_mb: int = Field(..., ge = MIN_UPLOAD_LIMIT_MB, le = MAX_UPLOAD_LIMIT_MB)\n\n\nclass UploadLimitResponse(BaseModel):\n    max_upload_size_mb: int\n    max_upload_size_bytes: int\n    max_upload_size_label: str\n    default_upload_size_mb: int\n    min_upload_size_mb: int = MIN_UPLOAD_LIMIT_MB\n    max_allowed_upload_size_mb: int = MAX_UPLOAD_LIMIT_MB\n\n\nclass HuggingFaceTokenPayload(BaseModel):","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/settings.py#L438-L474","documentation":"HTTP 422 raised directly by the DELETE /settings/generation-presets/{kind}/custom route (not pydantic) after it strips the query parameter name. It rejects empty names, the reserved name 'Default', and names longer than 80 characters — mirroring the create-side validator. A valid name that simply does not exist deletes nothing and still returns {'deleted': true}, so this 422 is purely about name shape.","triggerScenarios":"DELETE with name=\"\" or whitespace-only (URL-encoded %20), name=Default, or a name over 80 characters; often a frontend bug sending an untrimmed or empty string.","commonSituations":"Delete button clicked on a row whose name field is empty; sending the display label 'Default' instead of a real custom preset name; trailing whitespace introduced by copy-paste or URL encoding of spaces.","solutions":["Send the exact stored custom preset name, trimmed, 1-80 chars, not 'Default'.","Guard in the client: skip the DELETE when the row is the built-in default preset.","If names mismatch, GET the preset list first and delete by the returned name."],"exampleFix":"// before\nawait api.delete(`/settings/generation-presets/${kind}/custom?name=${raw}`); // raw = '  '\n\n// after\nconst name = raw.trim();\nif (name && name !== 'Default' && name.length <= 80) {\n  await api.delete(`/settings/generation-presets/${kind}/custom?name=${encodeURIComponent(name)}`);\n}","handlingStrategy":"validation","validationCode":"function validDeleteName(raw: string): boolean {\n  const name = raw.trim();\n  return name.length >= 1 && name.length <= 80 && name !== 'Default';\n}\nif (validDeleteName(row.name)) {\n  await api.delete(`/settings/generation-presets/${kind}/custom?name=${encodeURIComponent(row.name.trim())}`);\n}","typeGuard":"function isDeletablePresetName(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.delete(url); }\ncatch (e) {\n  if (e.status === 422) { skipRow(); return; } // name shape invalid — nothing to delete\n  throw e;\n}","preventionTips":["Delete only rows that came from the preset list GET, by their exact stored name.","Never offer delete for the built-in 'Default' row.","encodeURIComponent the name to survive spaces."],"tags":["fastapi","http-422","presets","validation","query-params"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}