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

  1. Sanitize the virtual path client-side: strip leading slashes, reject `..` segments, keep it relative to the thread root
  2. Verify the thread_id exists and belongs to the caller before issuing file calls
  3. If you are an internal trusted caller, pass the explicit `user_id` parameter instead of relying on ambient resolution
  4. 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

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


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/276fa9106dd3c16b. Report an issue: GitHub.