BerriAI/litellm · error · ValueError
Cannot route sensitive data without a session_id. Ensure the
Error message
Cannot route sensitive data without a session_id. Ensure the request includes a session_id in metadata or headers.
What it means
CustomGuardrail.raise_sensitive_data_route_exception() tries to build a SensitiveDataRouteException so the proxy can reroute the request to a safer model, but rerouting requires a session_id to keep the user's session sticky on the target model. The method calls _get_session_id_from_request_data() and, when it finds no session_id in the request's metadata or headers, raises this ValueError. Internally the proxy normally catches it and converts it to a blocking GuardrailRaisedException (error 361), but if you call this method directly you get the raw ValueError.
Source
Thrown at litellm/integrations/custom_guardrail.py:274
to route to an on-premise model instead of blocking.
The exception will reroute this request to the specified model. When
sticky_session_routing is enabled (the default), it also stores the
routing decision so subsequent requests in this session reuse the model.
Args:
route_to_model: The model to route this request (and session) to
request_data: The original request data dictionary
detection_info: Optional non-sensitive detection metadata (e.g. matched
entity types, rule ids, scores). This is surfaced in request metadata
and logs, so it must not contain the raw detected sensitive values.
Raises:
SensitiveDataRouteException: Always raises to trigger rerouting
"""
session_id: Final = self._get_session_id_from_request_data(request_data)
if not session_id:
raise ValueError(
"Cannot route sensitive data without a session_id. "
"Ensure the request includes a session_id in metadata or headers."
)
raise SensitiveDataRouteException(
route_to_model=route_to_model,
session_id=session_id,
guardrail_name=self.guardrail_name,
detection_info=detection_info,
sticky_session_routing=self.sticky_session_routing,
)
def _get_session_id_from_request_data(self, request_data: dict[str, Any]) -> str | None:
"""Extract session_id from request data."""
return get_session_id_from_request_data(request_data)
@staticmethod
def _scanned_text_hash(text: str) -> str:View on GitHub (pinned to 6c2dcb801b)
Solutions
- Include a session_id in the request: add 'session_id' to the 'metadata' field of the request body, or send it as a header the proxy copies into metadata
- If you call raise_sensitive_data_route_exception yourself, pass request_data that carries metadata.session_id (e.g. request_data['metadata']['session_id'] = uuid)
- If you cannot guarantee session_ids, remove sensitive_data_route_to_model / should_route_on_sensitive_data so the guardrail masks or blocks instead of routing
Example fix
# before
response = litellm.completion(
model="gpt-4o",
messages=[{"role": "user", "content": "my SSN is 123-45-6789"}],
metadata={"guardrails": ["presidio pii mask"]},
)
# after
response = litellm.completion(
model="gpt-4o",
messages=[{"role": "user", "content": "my SSN is 123-45-6789"}],
metadata={"guardrails": ["presidio pii mask"], "session_id": "user-abc-session-1"},
) Defensive patterns
Strategy: validation
Validate before calling
def has_session_id(request_data: dict) -> bool:
metadata = request_data.get("metadata") or {}
return bool(metadata.get("session_id"))
if guardrail.should_route_on_sensitive_data():
assert has_session_id(request_data), "session_id required for sensitive-data routing" Try / catch
try:
guardrail.raise_sensitive_data_route_exception(model, request_data, info)
except ValueError as e:
# no session: decide explicitly — block, mask, or attach a session and retry
handle_missing_session(request_data, str(e)) Prevention
- Standardize on always sending metadata.session_id with every proxied request
- Wrap guardrail route calls in a helper that injects a session_id before invoking them
- In tests, assert _get_session_id_from_request_data(request_data) is truthy before exercising route-mode guardrails
When it happens
Trigger: A guardrail with sensitive_data_route_to_model set detects PII and calls raise_sensitive_data_route_exception on a request where _get_session_id_from_request_data(request_data) returns None — i.e. no 'session_id' key in metadata (or litellm_metadata) and no x-litellm-session-id style header. Also hit when a custom guardrail subclass invokes this API from its own hooks on raw request dicts that never passed through the proxy session layer.
Common situations: Running bedrock/presidio/pii guardrails in 'route' mode via the LiteLLM proxy while clients call /chat/completions directly without a session_id; teams migrating from mask-on-detect to reroute-on-detect configs without adding session tracking; SDK calls that pass metadata but omit session_id.
Related errors
- Sensitive data detected by {self.guardrail_name} (routing sk
- Sensitive data detected by {self.guardrail_name}
- Unclassified keys in {PRICES_PATH.name}: {', '.join(unclassi
- custom_llm_provider is required
- litellm_params is required
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/3fbb8c9d1474bcff.
Report an issue: GitHub.