unslothai/unsloth · error · HTTPException

Could not save the change to this video.

Error message

Could not save the change to this video.

What it means

500 from the patch route: video_gallery.set_flags raised OSError while persisting the pin/archive flag — the flags store (a file/DB backing pin+archive state) could not be written. The comment explains the severity choice: the client applied the change optimistically, so silently swallowing the write failure would make the UI show a state that reverts on reload.

Source

Thrown at studio/backend/routes/video.py:595

@router.patch("/video/gallery/{video_id}", response_model = GalleryVideo)
async def update_gallery_video_flags(
    video_id: str,
    patch: GalleryFlagsPatch,
    current_subject: str = Depends(get_current_subject),
):
    """Pin/unpin or archive/restore one clip. Omitted fields are left alone."""
    from core.inference import video_gallery

    try:
        record = await asyncio.to_thread(
            video_gallery.set_flags, video_id, pinned = patch.pinned, archived = patch.archived
        )
    except OSError as exc:
        # The client already applied this optimistically, so a silent miss would look like it stuck
        # and then quietly undo on reload.
        logger.warning("video_gallery.set_flags_failed: %s", exc)
        raise HTTPException(status_code = 500, detail = "Could not save the change to this video.")
    if record is None:
        raise HTTPException(status_code = 404, detail = "Video not found.")
    # Archiving takes the clip off the strip, so the completed-job record must go with it: the page
    # merges that snapshot on mount, which would keep resurrecting the clip it just archived.
    if patch.archived:
        _forget_terminal_video(video_id)
    return GalleryVideo(**record)


@router.delete("/video/gallery/{video_id}")
async def delete_gallery_video(video_id: str, current_subject: str = Depends(get_current_subject)):
    from core.inference import video_gallery

    deleted = await asyncio.to_thread(video_gallery.delete, video_id)
    if not deleted:
        raise HTTPException(status_code = 404, detail = "Video not found.")
    _forget_terminal_video(video_id)
    return {"deleted": True}

View on GitHub (pinned to 203007d190)

Solutions

  1. Check the server log line 'video_gallery.set_flags_failed: %s' — it carries the underlying OSError.
  2. Fix the storage problem it names: chmod/chown the gallery data dir, free disk space, close the file locker.
  3. After fixing, re-toggle the flag so the persistent state matches the UI (or reload to resync the UI to truth).

Example fix

# diagnose
$ df -h ~/.unsloth/studio   # disk full?
$ ls -la ~/.unsloth/studio/video-gallery*  # permissions?
# fix
$ chown -R $(whoami) ~/.unsloth/studio
Defensive patterns

Strategy: try-catch

Try / catch

// optimistic UI must be revertible
const prev = clip.pinned;
clip.pinned = next;
try { await api.patch(`/video/gallery/${clip.id}`, { pinned: next }); }
catch (e) {
  clip.pinned = prev;              // roll back the optimistic change
  toast('Could not save the change to this video.');
}

Prevention

When it happens

Trigger: PATCH /video/gallery/{id} with pinned/archived while the flags file is read-only, its directory was deleted, the disk is full, or a permission/lock problem (e.g. flags JSON on a synced/locked path) blocks the write.

Common situations: Studio data directory owned by root after running once under sudo; disk-full on a long-lived box; OneDrive/Dropbox-style sync holding a lock on the flags file; moving STUDIO_HOME without moving permissions.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/15cc820abd217e17. Report an issue: GitHub.