v1.2.4
[v1.2.4] - 2026-07-04
Compatible with Open WebUI v0.10.0
Caution
This release fixes three bugs that could destroy live user data (a storage-wipe path, shared
knowledge bases being gutted when their uploader's account was removed, and live KB search
embeddings being deleted on every real run) plus a bug that made age-based chat pruning
silently ineffective on SQLite, the default backend.
Upgrading is strongly recommended for all deployments. Update before your next run.
Important
鈿狅笍 prune-open-webui will be slowly PHASED OUT in favor of an Event Function for native Open WebUI compatibility 鈿狅笍
This allows for:
- a UI inside Open WebUI
- automatic deletions (e.g. if a chat gets deleted -> go check if any other chats can be deleted based on age fully automatically)
- easier setup
- more compatibility across deployment types (e.g. Kubernetes pods)
- less hassle when installing, as it is a plugin and doesn't require special setup
- decide between automatic deletions, on-demand deletions or both!
鉃★笍 You will be able to find the Event Function here when it releases.
Added
--orphan-file-grace-hours N(default: 24,0disables): files younger than N hours are never
classified as orphaned. Uploading a file and referencing it (sending the message, adding it to a
knowledge base) are two separate requests in Open WebUI, so a prune running in that window could
previously delete an upload the user was still attaching. Applies to the standalone script, the
interactive CLI and the preview counts.
Changed
- Orphaned-file rule is now reference-based only. A file is deleted only when nothing references
it (no knowledge base, chat, folder, channel message or model). Previously a file was also deleted
whenever its uploader's account no longer existed, even if the file was still part of another
user's live knowledge base or chat: offboarding a user could silently gut shared knowledge bases.
Who uploaded a file no longer plays any role in whether its data is still needed. - Inactive-user pruning no longer deletes the user's files eagerly. The reference-based rule
applies instead: uploads backing another user's live knowledge base, chat or channel survive
account deletion, and the user's own unreferenced files are reclaimed by the orphan pass of the
same run. - Vector collections now follow file rows, exactly like storage bytes. Any file row that
survives the sweep (referenced, inside the grace window, or deferred to the next run) keeps its
embeddings. Previously a grace-protected upload kept its row and bytes but silently lost its
vectors in the same run, leaving a file that looks intact but returns nothing in retrieval. - Storage-orphan sweep preserves every existing file row. The upload cleanup now protects the
storage object of every file row in the database (unreferenced rows are removed by the database
pass first). Paths are matched raw, normalized, and by basename, so a non-canonicalUPLOAD_DIR
or a migrated data directory (pip to Docker, moved volume) can no longer cause every ref to
mismatch and classify all uploads as orphans. - Synchronous vector-store and storage calls inside async code paths (per-file collection drops,
bucket listing, physical deletion, preview counting) now run in worker threads, so the engine no
longer stalls the event loop when embedded in a server process.
Fixed
- A transient database error during the storage sweep could delete every upload. The active-path
lookup swallowed all exceptions and returned an empty preservation set, causing the sweep to treat
every object in local/S3/GCS/Azure storage as orphaned. It now aborts the pass instead. - SQLite: attachments of deleted chats were never reclaimed. Open WebUI does not enable SQLite
foreign-key enforcement, so deleting a chat leaves itschat_filerows behind, and the reference
scan trusted them: the attachments stayed pinned as "in use" forever, no matter how many sweeps
ran. Danglingchat_file/knowledge_file/channel_filerows are now cleaned before every sweep
and the reference scans verify the parent row still exists. - Knowledge-base embedding cleanup on file deletion never actually ran. It called a method that
exists on no cleaner class; the error was swallowed at debug level, so deleted files left their
chunks behind in every referencing knowledge-base collection. Deletion now goes through the
underlying vector client. - Completed the shared
knowledge-basescollection preservation fix (#37, #39) for the Milvus and
Qdrant multitenancy modes, which were still deleting the search-metadata embeddings of live
knowledge bases on every real run. - Pruned users left their login credentials in the database. Inactive-user deletion went through
a code path that removes the user row but not theauthrow, so the email and password hash of
every pruned account survived forever. Deletion now uses the same path as Open WebUI's own admin
delete, which removes both. - Legacy AI-generated images in live chats were deleted. Older chats reference generated images
only by URL (/api/v1/files/{id}/content) with no id field, which the reference scan could not
see. URL-style references are now recognized, and thechannel_filejunction is scanned too, so
channel uploads whose message has not been posted yet are no longer classified as orphans. - Preview counts now match execution exactly. Rows with a
NULLowner were deleted at execute
time but never counted in the preview; on large instances the per-table counts could exceed
SQLite's bind-parameter limit and silently report zero while execution deleted normally; and
messages inside channels deleted as orphaned in the same run were missing from the totals. All
counts now stream rows with the same filters execution uses, and the age-based and orphan chat
passes only count chats whose deletion actually succeeded. - Orphan sweeps silently under-deleted behind paginated API calls: only the 30 most recent knowledge
bases and 50 most recent notes were ever examined, and a single legacy prompt row (NULLtags)
aborted the entire run. These passes now stream raw rows. - Orphaned knowledge bases (owner deleted) are now de-referenced from every model's knowledge list,
as the age-based KB path already did, so shared models no longer point at a deleted collection.
The interactive CLI also gates the KB row deletion on the vector drop succeeding, matching the
other frontends. - GCS/Azure storage objects with nested names (containing
/) are skipped during orphan cleanup:
the deletion helper extracts keys naively and could have removed a different, live blob in a
shared bucket. - Standard-mode Milvus cleanup failed on a fresh process because the pymilvus
defaultconnection
alias was never established; it is now connected on demand. - The
--helpepilog advertised a--delete-orphaned-filesflag that does not exist.