BerriAI/litellm · error · ValueError
Invalid identifier : contains disallowed characters
Error message
Invalid identifier {identifier!r}: contains disallowed characters What it means
ArizePhoenixClient sanitizes every identifier (prompt_id, prompt_version_id) before placing it into a request URL path. `_sanitize_id` rejects identifiers containing '/', '\\', '#', or '?' because those characters would alter the URL path structure or query, then URL-encodes everything else. This is a guard against malformed or injected identifiers.
Solutions
- Use the bare prompt/prompt-version identifier with no '/', '\\', '#', '?' characters (e.g. 'UHJvbXB0OjE' style ID from the Phoenix UI)
- If the ID came from a URL, strip everything except the last path segment and any query/fragment before passing it
- Validate user-provided prompt IDs at your API boundary before they reach litellm's prompt manager
Example fix
# before prompt_id = "https://app.phoenix.arize.com/prompts/UHJvbXB0OjE" # contains '/' -> ValueError # after prompt_id = "UHJvbXB0OjE" # bare identifier only
Defensive patterns
Strategy: type-guard
Validate before calling
import re
def safe_prompt_id(identifier: str) -> str:
if not re.fullmatch(r"[A-Za-z0-9_\-.]+", identifier):
raise ValueError(f"prompt_id {identifier!r} contains path/query characters")
return identifier
prompt_id = safe_prompt_id(user_supplied_prompt_id) Type guard
def is_sanitizable_identifier(identifier: str) -> bool:
return isinstance(identifier, str) and not any(c in identifier for c in ("/", "\\", "#", "?")) and ".." not in identifier Try / catch
try:
client.get_prompt_version(prompt_version_id)
except ValueError as e:
if "disallowed characters" in str(e):
# strip to last path segment and retry once with the bare ID
bare = prompt_version_id.rstrip("/#?").rsplit("/", 1)[-1]
if bare and is_sanitizable_identifier(bare):
return client.get_prompt_version(bare)
raise Prevention
- Validate prompt IDs at your API boundary with an allow-list regex before they reach litellm
- Store bare Phoenix IDs in config; never paste URLs
When it happens
Trigger: Calling prompt operations with an identifier that contains a path separator or query character, e.g. prompt_id='workspace/prompts/123', 'my\\prompt', 'prompt#v2', or 'prompt?version=1'. The client is constructed via the Arize Phoenix prompt integration when loading or rendering that prompt.
Common situations: Pasting a full Phoenix URL or a prompt 'slug path' as the prompt_id instead of the bare ID; copy-pasting IDs with trailing URL fragments; user-supplied prompt IDs from an API request being passed through unvalidated.
Related errors
- Invalid identifier : path traversal detected
- Access denied to prompt version
- api_key and api_base are required for Arize Phoenix prompt…
- Authentication failed. Check your Arize Phoenix API key and…
- Empty or unsafe filename
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/b47626248f053f8d.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/integrations/arize/arize_phoenix_client.py:14
"""
Arize Phoenix API client for fetching prompt versions from Arize Phoenix.
"""
import urllib.parse
from typing import Any, Final
from litellm.llms.custom_httpx.http_handler import HTTPHandler
def _sanitize_id(identifier: str) -> str:
"""Reject path traversal characters and URL-encode the identifier."""
if any(c in identifier for c in ("/", "\\", "#", "?")):
raise ValueError(f"Invalid identifier {identifier!r}: contains disallowed characters")
if ".." in identifier:
raise ValueError(f"Invalid identifier {identifier!r}: path traversal detected")
return urllib.parse.quote(identifier, safe="")
class ArizePhoenixClient:
"""
Client for interacting with Arize Phoenix API to fetch prompt versions.
Supports:
- Authentication with Bearer tokens
- Fetching prompt versions
- Direct API base URL configuration
"""
def __init__(self, api_key: str | None = None, api_base: str | None = None):
"""
Initialize the Arize Phoenix client.View on GitHub (pinned to 77b7c6c40c)