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
- Use ids returned by the upload API only; directory names are never valid ids
- List valid ids via the upload/gallery listing endpoints when in doubt
- 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
- Don't derive ids from filenames — directories share the namespace
- Prune client caches of deleted ids on 404
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
- Access denied
- No files uploaded
- data?.error || data?.detail || `HTTP ${res.status}`
- (data && (data.error || data.detail)) || `HTTP ${res.status}
- Failed
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/f34bda67f5741b8e.
Report an issue: GitHub.