BerriAI/litellm · warning · ValueError

Invalid file path {file_path!r}: contains URL special charac

Error message

Invalid file path {file_path!r}: contains URL special characters

What it means

A deliberate security guard in _sanitize_file_path: before URL-encoding path segments for the BitBucket raw-file API, the function rejects any file path containing '#' or '?'. Those characters have special meaning in URLs and could otherwise alter the request target (e.g. truncate the path at a fragment or inject query params), so they are treated as invalid input rather than escaped.

Source

Thrown at litellm/integrations/bitbucket/bitbucket_client.py:15

"""
BitBucket API client for fetching .prompt files from BitBucket repositories.
"""

import base64
import urllib.parse
from typing import Any, Final

from litellm.llms.custom_httpx.http_handler import HTTPHandler


def _sanitize_file_path(file_path: str) -> str:
    """Reject path traversal and URL-encode each path segment."""
    if "#" in file_path or "?" in file_path:
        raise ValueError(f"Invalid file path {file_path!r}: contains URL special characters")
    parts: Final = file_path.split("/")
    for part in parts:
        if part == "..":
            raise ValueError(f"Invalid file path {file_path!r}: path traversal detected")
    return "/".join(urllib.parse.quote(part, safe="") for part in parts)


class BitBucketClient:
    """
    Client for interacting with BitBucket API to fetch .prompt files.

    Supports:
    - Authentication with access tokens
    - Fetching file contents from repositories
    - Team-based access control through BitBucket permissions
    - Branch-specific file fetching
    """

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Remove '#' and '?' from the file path / prompt_id used to address the file
  2. Rename the repository file if it actually contains those characters
  3. Sanitize external input before passing it as a path (strip fragments/queries at your API boundary)

Example fix

# before
content = client.get_file("prompts/summarize#latest.prompt")  # ValueError

# after
content = client.get_file("prompts/summarize-latest.prompt")
Defensive patterns

Strategy: validation

Validate before calling

def validate_file_path(file_path: str) -> str:
    if "#" in file_path or "?" in file_path:
        raise ValueError(f"path must not contain '#' or '?': {file_path!r}")
    return file_path

path = validate_file_path(user_supplied_path)
content = client.get_file(path)

Type guard

def is_safe_prompt_path(p: str | None) -> bool:
    if not isinstance(p, str) or not p:
        return False
    return "#" not in p and "?" not in p and ".." not in p.split("/") and p.endswith(".prompt")

Try / catch

try:
    client.get_file(path)
except ValueError as e:
    if "URL special characters" in str(e):
        path = path.split("#")[0].split("?")[0]  # or reject input outright
        raise ValueError("reject user paths containing fragments/queries") from e
    raise

Prevention

When it happens

Trigger: Calling get_file (directly or via the BitBucket prompt manager) with a file path containing '#' or '?', e.g. 'prompts/summarize#v2.prompt' or 'faq/q?a.prompt'; user-supplied prompt paths passed through without sanitization; prompt_id strings copied from URLs that include fragments.

Common situations: Prompt IDs derived from URLs or user input containing anchors/query strings; files genuinely named with '#' or '?' (must be renamed — the guard is unconditional); testing the integration with hand-crafted paths.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/e6b6c8e6ba91048c. Report an issue: GitHub.