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

  1. 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
  2. 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)
  3. 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

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


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