AUTOMATIC1111/stable-diffusion-webui · error · ValueError

File cannot be fetched: {filename}. Must be in one of direct

Error message

File cannot be fetched: {filename}. Must be in one of directories registered by extra pages.

What it means

Second guard in fetch_file(): the requested filename's absolute path must have one of the registered allowed_dirs (directories declared by extra-network pages via allowed_directories_for_previews()) among its parents. Anything outside those roots — including ../ traversal attempts — is rejected with this ValueError, making it also a path-traversal defense.

Source

Thrown at modules/ui_extra_networks.py:104

    return res

def register_page(page):
    """registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions"""

    extra_pages.append(page)
    allowed_dirs.clear()
    allowed_dirs.update(set(sum([x.allowed_directories_for_previews() for x in extra_pages], [])))


def fetch_file(filename: str = ""):
    from starlette.responses import FileResponse

    if not os.path.isfile(filename):
        raise HTTPException(status_code=404, detail="File not found")

    if not any(Path(x).absolute() in Path(filename).absolute().parents for x in allowed_dirs):
        raise ValueError(f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages.")

    ext = os.path.splitext(filename)[1].lower()[1:]
    if ext not in allowed_preview_extensions():
        raise ValueError(f"File cannot be fetched: {filename}. Extensions allowed: {allowed_preview_extensions()}.")

    # would profit from returning 304
    return FileResponse(filename, headers={"Accept-Ranges": "bytes"})


def fetch_cover_images(page: str = "", item: str = "", index: int = 0):
    from starlette.responses import Response

    page = next(iter([x for x in extra_pages if x.name == page]), None)
    if page is None:
        raise HTTPException(status_code=404, detail="File not found")

    metadata = page.metadata.get(item)
    if metadata is None:

View on GitHub (pinned to 82a973c043)

Solutions

  1. Only request files that live inside a registered extra-networks directory (embeddings/, models/Lora/, etc.)
  2. If you author an extra-networks page, implement allowed_directories_for_previews() to return your preview dirs so they become fetchable
  3. For external assets, serve them from your own route instead of trying to use this endpoint

Example fix

# extension authors: register your preview directory
class MyPage(extra_networks_page.ExtraNetworksPage):
    def allowed_directories_for_previews(self):
        return [os.path.join(my_extension_dir, 'previews')]
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
from modules import ui_extra_networks
def is_fetchable(filename):
    p = Path(filename).absolute()
    return p.is_file() and any(Path(x).absolute() in p.parents for x in ui_extra_networks.allowed_dirs)

Prevention

When it happens

Trigger: A request whose filename resolves outside every allowed root: absolute paths pointing elsewhere on disk (/etc/passwd), or relative paths with enough ../ segments to climb out of the models directory. Also legitimate-looking requests when allowed_dirs is empty because no extra page has registered preview directories yet.

Common situations: Security scanners probing the endpoint; a custom extension registering a page but forgetting allowed_directories_for_previews(); bookmarked/crafted URLs referencing directories that were never registered.

Related errors


AI-assisted analysis of AUTOMATIC1111/stable-diffusion-webui@82a973c043 (2026-08-14). Data as JSON: /api/errors/37b57c9bfd66cc8b. Report an issue: GitHub.