{"record":{"id":"3fbb8c9d1474bcff","repo":"BerriAI/litellm","slug":"cannot-route-sensitive-data-without-a-session-id","errorCode":null,"errorMessage":"Cannot route sensitive data without a session_id. Ensure the request includes a session_id in metadata or headers.","messagePattern":"Cannot route sensitive data without a session_id\\. Ensure the request includes a session_id in metadata or headers\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/integrations/custom_guardrail.py","lineNumber":277,"sourceCode":"        to route to an on-premise model instead of blocking.\n\n        The exception will reroute this request to the specified model. When\n        sticky_session_routing is enabled (the default), it also stores the\n        routing decision so subsequent requests in this session reuse the model.\n\n        Args:\n            route_to_model: The model to route this request (and session) to\n            request_data: The original request data dictionary\n            detection_info: Optional non-sensitive detection metadata (e.g. matched\n                entity types, rule ids, scores). This is surfaced in request metadata\n                and logs, so it must not contain the raw detected sensitive values.\n\n        Raises:\n            SensitiveDataRouteException: Always raises to trigger rerouting\n        \"\"\"\n        session_id: Final = self._get_session_id_from_request_data(request_data)\n        if not session_id:\n            raise ValueError(\n                \"Cannot route sensitive data without a session_id. \"\n                \"Ensure the request includes a session_id in metadata or headers.\"\n            )\n\n        raise SensitiveDataRouteException(\n            route_to_model=route_to_model,\n            session_id=session_id,\n            guardrail_name=self.guardrail_name,\n            detection_info=detection_info,\n            sticky_session_routing=self.sticky_session_routing,\n        )\n\n    def _get_session_id_from_request_data(self, request_data: dict[str, Any]) -> str | None:\n        \"\"\"Extract session_id from request data.\"\"\"\n        return get_session_id_from_request_data(request_data)\n\n    @staticmethod\n    def _scanned_text_hash(text: str) -> str:","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/integrations/custom_guardrail.py#L259-L295","documentation":"For sensitive-data rerouting, CustomGuardrail.raise_sensitive_data_route_exception() must bind the flagged request to a sticky session on the alternate model. It first extracts a session id from the request data via get_session_id_from_request_data (metadata.session_id or the session headers); when none is found it raises this ValueError before the rerouting SensitiveDataRouteException is ever raised.","triggerScenarios":"Guardrail configured with sensitive_data_route_to_model and a request arrives without metadata['session_id'] and without a session header; direct SDK/API calls into the proxy that never established a session; session field name typos like 'sessionId' in metadata.","commonSituations":"Enabling sensitive-data routing on an existing traffic stream that does not yet propagate session ids; clients migrated from raw completions calls with no session concept; gateway stripping custom headers including the session one.","solutions":["Send metadata={'session_id': '<stable id>'} (or the proxy's session header) on every request that may carry sensitive data","Fix header/metadata propagation through your gateway so the session id survives to the guardrail","If traffic is genuinely sessionless, disable sensitive-data routing and use plain blocking/masking mode"],"exampleFix":"# before\nresponse = litellm.acompletion(model='gpt-4o', messages=[...])  # no session id -> ValueError on detection\n\n# after\nresponse = litellm.acompletion(\n    model='gpt-4o', messages=[...],\n    metadata={'session_id': user_conversation_id},  # enables sticky rerouting\n)","handlingStrategy":"validation","validationCode":"sid = request_data.get('metadata', {}).get('session_id')\nif not sid:\n    raise HTTPException(400, 'session_id required when sensitive-data routing is enabled')\nawait guardrail.raise_sensitive_data_route_exception(route_to_model, request_data, detection_info)","typeGuard":"def has_session_id(request_data: dict) -> bool:\n    meta = request_data.get('metadata') or {}\n    return bool(meta.get('session_id')) or bool(request_data.get('headers'))","tryCatchPattern":"except ValueError as e:\n    if 'session_id' in str(e):\n        retry the call once after attaching metadata={'session_id': str(uuid4())}\n    else:\n        raise","preventionTips":["Propagate a stable session_id in metadata for every conversation","Ensure gateways forward custom session headers","Reject sessionless requests early when routing mode is on"],"tags":["guardrail","session","sensitive-data","routing","litellm"],"backgroundTag":"missing-session-id","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}