odysseus-dev/odysseus · error · HTTPException

File not found

Error message

File not found

What it means

HTTP 404 from _resolve_upload_path when the direct path under upload_root exists but is not a regular file — file_id names a directory or special file. lexists succeeded and the inside-root check passed, but os.path.isfile failed.

Source

Thrown at routes/upload_routes.py:169

        from src.constants import UPLOAD_DIR
        return os.path.realpath(getattr(upload_handler, "upload_dir", UPLOAD_DIR))

    def _path_inside_upload_dir(path: str) -> bool:
        try:
            return os.path.commonpath([_upload_root(), os.path.realpath(path)]) == _upload_root()
        except Exception:
            return False

    def _resolve_upload_path(file_id: str) -> str:
        from src.constants import UPLOAD_DIR
        upload_root = getattr(upload_handler, "upload_dir", UPLOAD_DIR)
        direct = os.path.join(upload_root, file_id)
        if os.path.lexists(direct):
            if not _path_inside_upload_dir(direct):
                raise HTTPException(403, "Access denied")
            if os.path.isfile(direct):
                return direct
            raise HTTPException(404, "File not found")

        for root, _dirs, files in os.walk(upload_root, followlinks=False):
            if file_id not in files:
                continue
            path = os.path.join(root, file_id)
            if not _path_inside_upload_dir(path):
                raise HTTPException(403, "Access denied")
            if os.path.isfile(path):
                return path
            raise HTTPException(404, "File not found")

        raise HTTPException(404, "File not found")

    def _valid_session_id_for_owner(db, session_id: str | None, owner: str | None) -> str | None:
        if not session_id:
            return None
        sess = db.query(DbSession).filter(DbSession.id == session_id).first()
        if not sess:

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Use ids returned by the upload API only; directory names are never valid ids
  2. List valid ids via the upload/gallery listing endpoints when in doubt
  3. Remove stray non-file artifacts from the uploads root if one shadows a real id
Defensive patterns

Strategy: validation

Validate before calling

// verify the id refers to a listed upload before fetching
const files = await listUploads();
if (!files.some(f => f.id === fileId)) throw new Error('unknown id');

Type guard

function isKnownUploadId(id: string, known: string[]): boolean {
  return known.includes(id);
}

Try / catch

if (resp.status === 404) { removeFromLocalCache(fileId); }

Prevention

When it happens

Trigger: Download request whose file_id equals a directory name inside uploads (such as 'sessions' or 'gallery'), or a fifo/socket/device file occupying that name.

Common situations: Client constructing ids from filenames that happen to collide with directory names; upload id schema changed so old ids now collide with directory names; partial upload left a stub non-file artifact.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/f34bda67f5741b8e. Report an issue: GitHub.