unslothai/unsloth · warning · HTTPException
str(exc)
Error message
str(exc)
What it means
HTTP 409 from PUT /settings/generation-presets/{image|video}/custom. The route calls upsert_media_generation_preset and converts any ValueError into 409 with str(exc). The stored-preset store raises ValueError('Delete a preset before saving another one') when you add a NEW preset name while the readable-preset slots are already at the maximum count (_MAX_PRESETS). The lambda also re-validates that each stored preset round-trips through the current pydantic schema; unrelated schema failures surface as other errors.
Source
Thrown at studio/backend/routes/settings.py:430
lambda stored, submitted: _preserve_recovered_defaults(
VideoGenerationPresetState, stored, submitted
),
)
return {"saved": True}
def _upsert_custom_generation_preset(
kind: Literal["image", "video"], payload: ImageGenerationPreset | VideoGenerationPreset
) -> dict[str, bool]:
try:
schema = type(payload)
upsert_media_generation_preset(
kind,
payload.model_dump(),
lambda stored: _validated_readable_model(schema, stored) is not None,
)
except ValueError as exc:
raise HTTPException(status_code = 409, detail = str(exc)) from exc
return {"saved": True}
@router.put("/generation-presets/image/custom")
def upsert_custom_image_generation_preset(
payload: ImageGenerationPreset, current_subject: str = Depends(get_current_subject)
) -> dict[str, bool]:
return _upsert_custom_generation_preset("image", payload)
@router.put("/generation-presets/video/custom")
def upsert_custom_video_generation_preset(
payload: VideoGenerationPreset, current_subject: str = Depends(get_current_subject)
) -> dict[str, bool]:
return _upsert_custom_generation_preset("video", payload)
@router.delete("/generation-presets/{kind}/custom")View on GitHub (pinned to 203007d190)
Solutions
- DELETE /settings/generation-presets/{kind}/custom?name=<old> for a preset you no longer need, then retry the PUT.
- Overwrite an existing preset instead of adding a new one — same name replaces in place and never hits the cap.
- Show the preset count/limit in the UI and disable 'Save as new' when full.
Example fix
// before
await api.put('/settings/generation-presets/image/custom', { name: `Style ${n+1}`, params }); // 409 cap
// after
if (presets.length >= MAX_PRESETS) {
await api.delete(`/settings/generation-presets/image/custom?name=${encodeURIComponent(oldest)}`);
}
await api.put('/settings/generation-presets/image/custom', { name: `Style ${n+1}`, params }); Defensive patterns
Strategy: validation
Validate before calling
const existing = await api.getCustomPresets(kind);
const isReplace = existing.some(p => p.name === payload.name);
if (!isReplace && existing.length >= MAX_PRESETS) {
throw new Error('Preset limit reached — delete one first');
}
await api.put(`/settings/generation-presets/${kind}/custom`, payload); Type guard
function canSavePreset(list: { name: string }[], name: string, max: number): boolean {
return list.some(p => p.name === name) || list.length < max;
} Try / catch
try { await api.put(presetUrl, payload); }
catch (e) {
if (e.status === 409 && /Delete a preset/.test(e.detail)) { promptDeleteOldest(); return; }
throw e;
} Prevention
- Track the preset count and cap in the client UI.
- Prefer overwriting an existing name over creating near-duplicates.
- Offer delete-from-list inline so the cap never surprises the user.
When it happens
Trigger: PUT a custom image or video generation preset with a name that does not already exist while the kind already stores the maximum number of readable custom presets; hitting the cap after recovering/migrating presets; repeatedly saving presets without deleting.
Common situations: Users accumulating generation presets up to the cap; teams importing preset collections; UI not surfacing the remaining-preset count.
Related errors
- Preset name is reserved or empty
- Invalid preset name
- fill_absent_fields cannot be combined with remove.
- Could not verify {model!r} as an embedding model on Hugging
- unsupported file type: {ext}. allowed: {allowed}
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/57cdfcbe452f6746.
Report an issue: GitHub.