open-webui / open-webui/open-webui

bug: Importing open_webui.config deletes every tracked file in backend/open_webui/static/ when the frontend is not built

Open Beginner friendly
#29,968 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug confirmed issue
Dominant language
Python
Stars
153k
Forks
22.3k
Avg merge
1d 4h
Merged PRs (30d)
194

Description

Bug

backend/open_webui/config.py clears STATIC_DIR at import time before checking whether there is a frontend build to replace it with. In a source checkout there is no build/static/, so the copy loop that follows finds nothing and the files are simply gone.

The result is that importing open_webui.config deletes 17 of the 32 files this repository tracks under backend/open_webui/static/ — including BRANDING.md, README.md, custom.css, every favicon, logo.png, splash.png, site.webmanifest, loader.js, user.png and user-import.csv. iterdir() is not recursive, so assets/, fonts/ and swagger-ui/ survive, which makes the loss look partial and easy to misread.

It fails silently. Both loops swallow every exception with pass, nothing is logged, and the import succeeds. The only symptom is a dirty git status.

Steps to reproduce

From a clean checkout of main, with backend dependencies installed and without running npm run build:

git status --porcelain backend/open_webui/static/    # clean

cd backend
WEBUI_SECRET_KEY=test DATA_DIR=/tmp/owui-data \
  python -c "import open_webui.config"

cd ..
git status --porcelain backend/open_webui/static/    # 17 deletions

Anything that imports the module is enough — pytest collecting the backend test suite, a linter that imports, an editor language server, python -c "import open_webui.main".

Expected: importing a config module does not delete tracked files from the working tree.

Actual: 17 tracked files are deleted, silently.

Cause

backend/open_webui/config.py (lines 99-110 on main at 0.11.3):

try:
    if STATIC_DIR.exists():
        for item in STATIC_DIR.iterdir():
            if item.is_file() or item.is_symlink():
                try:
                    item.unlink()
                except Exception as e:
                    pass          # silent
except Exception as e:
    pass                          # silent

for file_path in (FRONTEND_BUILD_DIR / 'static').glob('**/*'):   # nothing to copy
    ...

The wipe is unconditional; only the copy is conditional on the build existing. Introduced in 4cee7c29a5.

The intent is clear and reasonable — replace stale copied assets so a rebuilt frontend's static/ wins — but the order means the "replace" half can be skipped while the "delete" half always runs.

Suggested fix

Make the wipe conditional on the same thing the copy is conditional on, so delete and replace succeed or are skipped together:

if (FRONTEND_BUILD_DIR / 'static').is_dir():
    try:
        if STATIC_DIR.exists():
            for item in STATIC_DIR.iterdir():
                ...

A container build is unaffected: build/static/ exists, the wipe runs, the fresh assets replace the old ones. A source checkout keeps its files.

Two smaller points, if you want to harden it further:

  • The bare except Exception: pass on both loops is what made this invisible for so long. Even a logging.debug on the unlink failure path would have surfaced it.
  • Deleting files from the package directory at import time is surprising in itself; moving this into an explicit startup step (or main.py's lifespan) would keep a plain import open_webui.config side-effect-free for tooling.

Environment

  • Open WebUI: 0.11.3 (main)
  • Python 3.11, Linux
  • Reproduced in a source checkout with no frontend build present

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in backend/open_webui/config.py around lines 99-110 and inspect the cleanup and frontend static-copy paths. Run the provided import reproduction from a clean checkout, then verify that importing without build/static preserves backend/open_webui/static/ files while a container build with build/static/ still replaces stale assets. Confirm the result with git status.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.