BerriAI/litellm · error · ValueError
Category file path '{path}' is outside the allowed categorie
Error message
Category file path '{path}' is outside the allowed categories directory '{categories_dir}' What it means
ValueError from _assert_within_categories_dir raised when the realpath-resolved category file path does not share the categories directory prefix — the file genuinely escapes the litellm_content_filter module directory after symlink resolution or traversal. This is a deliberate security jail: category_file values are untrusted config inputs and must not read arbitrary files (e.g. ../../../../etc/passwd).
Source
Thrown at litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py:355
return {
"category": cat_config.get("category"),
"enabled": cat_config.get("enabled", True),
"action": cat_config.get("action"),
"category_file": cat_config.get("category_file"),
}
@staticmethod
def _assert_within_categories_dir(path: str, categories_dir: str) -> None:
"""Raise ValueError if path escapes the categories directory."""
resolved: Final = os.path.realpath(path)
allowed: Final = os.path.realpath(categories_dir)
try:
common: Final = os.path.commonpath([resolved, allowed])
except ValueError:
# commonpath() raises ValueError on Windows when paths span different drives
raise ValueError(f"Category file path '{path}' is outside the allowed categories directory")
if common != allowed:
raise ValueError(
f"Category file path '{path}' is outside the allowed categories directory '{categories_dir}'"
)
def _resolve_category_file_path(self, file_path: str) -> str:
"""
Resolve a category file path that may be relative.
Paths in policy templates (e.g. category_file) are often stored as
relative paths like "litellm/proxy/.../policy_templates/file.yaml".
These only work when the CWD is the project root. In production
(Docker, installed packages, etc.) the CWD is different, so the
file isn't found.
Resolution order:
1. Return as-is if absolute or already exists (jailed to module dir).
2. Try joining the full path relative to this module's directory (jailed).
3. Progressively strip leading path components and try each suffix
relative to this module's directory (jailed).View on GitHub (pinned to 77b7c6c40c)
Solutions
- Place custom category files inside the litellm_content_filter module directory (a subdirectory of it is fine) and reference them with a relative path.
- If files legitimately live on a mounted volume outside the package (trusted environments only), set LITELLM_CONTENT_FILTER_ALLOW_EXTERNAL_PATHS=true — understand this disables the traversal jail.
- Fix the category_file value: remove ../ segments and absolute paths pointing outside the categories dir.
- Replace symlinks that point outside the module dir with real files inside it.
Example fix
# before category_file: '../../../../etc/custom_policies/secret_category.yaml' # after — file shipped inside the module dir category_file: 'categories/secret_category.yaml'
Defensive patterns
Strategy: validation
Validate before calling
# Pre-flight the same jail the guardrail applies: realpath containment
import os
def within_categories_dir(path: str, module_dir: str) -> bool:
resolved = os.path.realpath(path)
allowed = os.path.realpath(module_dir)
try:
return os.path.commonpath([resolved, allowed]) == allowed
except ValueError:
return False
# assert within_categories_dir(category_file, pkg_dir), f"{category_file} escapes the categories dir" Prevention
- Ship custom category YAMLs inside the litellm_content_filter module directory and use relative paths.
- Never build category_file from user input — the jail exists precisely to stop traversal reads.
- If you must use mounted volumes, document LITELLM_CONTENT_FILTER_ALLOW_EXTERNAL_PATHS=true as an explicit, trusted-env trade-off.
- Avoid symlinks inside the module dir that point outside — realpath resolution defeats them silently here as errors.
When it happens
Trigger: A policy template or config sets category_file to an absolute path outside the package (e.g. /etc/policies/cat.yaml), a relative path containing ../ traversal, or a path inside the module dir that is a symlink pointing elsewhere — and LITELLM_CONTENT_FILTER_ALLOW_EXTERNAL_PATHS is unset.
Common situations: Docker deployments mounting custom category YAMLs to a path outside the package and referencing them absolutely; penetration tests probing traversal; relative paths that stop resolving inside the module dir once production CWD differs from the project root.
Related errors
- Invalid identifier {identifier!r}: path traversal detected
- Invalid file path {file_path!r}: path traversal detected
- {field_name} cannot be a dot path segment
- git-subdir 'path' must be a relative path of the form 'segme
- Category file path '{path}' is outside the allowed categorie
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/89d379d3747922cc.
Report an issue: GitHub.