HumanSignal/label-studio · error · Http404

Directory indexes are not allowed here.

Error message

Directory indexes are not allowed here.

What it means

The static file serving view (serve in static_serve.py) resolves the requested path under document_root via safe_join; if the resolved fullpath is a directory, it raises Http404 with 'Directory indexes are not allowed here.' Directory listing is deliberately disabled for security.

Source

Thrown at label_studio/core/utils/static_serve.py:61

    also set ``show_indexes`` to ``True`` if you'd like to serve a basic index
    of the directory.  This index view will use the template hardcoded below,
    but if you'd like to override it, you can create a template called
    ``static/directory_index.html``.

    If manifest_asset_prefix is provided, we will try to serve the file from the manifest.json
    if the file is not found in the document_root.

    Example:
        path = "main.js"
        document_root = "/dist/apps/labelstudio/"
        manifest_asset_prefix = "react-app"
        manifest_json = {"main.js": "/react-app/main.123456.js"}
        fullpath = Path(safe_join(document_root, "main.123456.js"))
    """
    path = posixpath.normpath(path).lstrip('/')
    fullpath = Path(safe_join(document_root, path))
    if fullpath.is_dir():
        raise Http404(_('Directory indexes are not allowed here.'))
    if manifest_asset_prefix and not fullpath.exists():
        possible_asset = get_manifest_asset(path)
        manifest_asset_prefix = (
            f'/{manifest_asset_prefix}' if not manifest_asset_prefix.startswith('/') else manifest_asset_prefix
        )
        if possible_asset.startswith(manifest_asset_prefix):
            possible_asset = possible_asset[len(manifest_asset_prefix) :]
        fullpath = Path(safe_join(document_root, possible_asset))
    if not fullpath.exists():
        raise Http404(_('“%(path)s” does not exist') % {'path': fullpath})
    # Respect the If-Modified-Since header.
    statobj = fullpath.stat()
    if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), statobj.st_mtime):
        return HttpResponseNotModified()
    content_type, encoding = static_file_content_type_and_encoding(str(fullpath))

    response = RangedFileResponse(request, fullpath.open('rb'), content_type=content_type)
    response['Last-Modified'] = http_date(statobj.st_mtime)

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Request the actual file path, not the directory, e.g. /static/js/app.js.
  2. Check that asset URLs/href attributes in templates include the filename.
  3. Verify the reverse proxy/CDN is not stripping the filename from the path.
  4. Return 404 gracefully in your error handler and link to a valid index asset if you need an entry page.

Example fix

// before
<link rel="stylesheet" href="/static/css/">

// after
<link rel="stylesheet" href="/static/css/main.css">
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse
import posixpath
def is_file_url(static_base, url):
    path = posixpath.normpath(urlparse(url).path).rstrip('/')
    return path.startswith(static_base) and posixpath.basename(path) != ''

Type guard

import posixpath
def points_to_file(path: str) -> bool:
    return posixpath.basename(posixpath.normpath(path)) != ''

Try / catch

from django.http import Http404
try:
    return serve(request, path, document_root)
except Http404 as e:
    logging.warning('Static 404: %s (%s)', path, e)
    return HttpResponseNotFound('Asset not found')

Prevention

When it happens

Trigger: Requesting a URL that maps to a directory under the static root, e.g. GET /static/js/ or /static/ with no file portion, or a path whose normalization lands on a directory (e.g. /static/js/../js/).

Common situations: Testing the static server by opening a folder URL in a browser; misconfigured base URLs pointing at a directory; broken asset links missing the filename (e.g. /static/css/ instead of /static/css/app.css).

Related errors


AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29). Data as JSON: /api/errors/743f9530708d113f. Report an issue: GitHub.