bytedance/deer-flow · error · HTTPException
{e}
Error message
{e} What it means
Wraps `ValueError` from `resolve_virtual_path` into an HTTPException: 403 when the message contains 'traversal' (path escapes allowed directories), otherwise 400 for other invalid path input. The detail text is the original ValueError message, so the underlying cause is visible to the caller.
Source
Thrown at backend/app/gateway/path_utils.py:32
Args:
thread_id: The thread ID.
virtual_path: The virtual path as seen inside the sandbox
(e.g., /mnt/user-data/outputs/file.txt).
user_id: The user whose storage to resolve under. Defaults to the
effective user when not given; callers acting on behalf of a
specific owner (e.g. trusted internal callers) pass it explicitly.
Returns:
The resolved filesystem path.
Raises:
HTTPException: If the path is invalid or outside allowed directories.
"""
try:
return get_paths().resolve_virtual_path(thread_id, virtual_path, user_id=user_id or get_effective_user_id())
except ValueError as e:
status = 403 if "traversal" in str(e) else 400
raise HTTPException(status_code=status, detail=str(e))
View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Sanitize the virtual path client-side: strip leading slashes, reject `..` segments, keep it relative to the thread root
- Verify the thread_id exists and belongs to the caller before issuing file calls
- If you are an internal trusted caller, pass the explicit `user_id` parameter instead of relying on ambient resolution
- Read the returned detail message — it states exactly which constraint (traversal vs invalid) failed
Example fix
// before
const p = userInput; // e.g. '../../secrets.env'
await api.writeFile(threadId, p, data);
// after
const safe = userInput.replace(/\\..\\/g, '');
if (userInput.includes('..')) throw new Error('bad path');
await api.writeFile(threadId, safe, data); Defensive patterns
Strategy: validation
Validate before calling
function isSafeVirtualPath(p: string): boolean {
return p !== '' && !p.startsWith('/') && !p.split('/').includes('..') && !p.includes('\\');
}
if (!isSafeVirtualPath(virtualPath)) throw new Error(`unsafe path: ${virtualPath}`); Type guard
const isSafeVirtualPath = (p: string): boolean =>
typeof p === 'string' && p.length > 0 && !p.startsWith('/') &&
p.split('/').every((seg) => seg !== '..' && seg !== '.' && !seg.includes('\\')); Try / catch
try {
await api.writeFile(threadId, vpath, data);
} catch (e) {
if (e.status === 403 && /traversal/i.test(e.detail)) throw new UserError('path escapes allowed dirs');
if (e.status === 400) throw new UserError(`invalid path: ${e.detail}`);
throw e;
} Prevention
- Always build virtual paths from sanitized, user-independent components
- Reject '..' and absolute paths before any file API call
- Verify thread ownership before issuing per-thread file operations
When it happens
Trigger: Passing a `virtual_path` containing `..` segments that resolve outside the thread/user scope; supplying an empty or malformed path; providing a `thread_id` that is invalid or unknown to the path resolver; a user_id mismatch when resolving another user's paths without explicit trusted-caller override.
Common situations: Client-side joined file paths leaking absolute paths or parent segments; agents echoing user-supplied filenames into path APIs; a thread_id from a different/older deployment being reused after data was cleared.
Related errors
- Path is not a file: {path}
- Invalid provider ID
- Missing code or state parameter
- URL is required
- Input text is required
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/276fa9106dd3c16b.
Report an issue: GitHub.