{"record":{"id":"3df6ed53cd901d58","repo":"BerriAI/litellm","slug":"callback-param-param-from-source-contains","errorCode":null,"errorMessage":"Callback param '{param}' (from {source}) contains an 'os.environ/' reference. Environment references in request-supplied parameters are no longer resolved server-side for security reasons.\\nTo resolve:\\n  1. Remove the 'os.environ/' reference from your request body / metadata.\\n  2. Either (a) configure this callback value in your proxy config.yaml under 'litellm_settings' / 'general_settings', or (b) pass the resolved secret value directly in the request.\\nSee https://docs.litellm.ai/docs/proxy/logging for server-side callback configuration.","messagePattern":"Callback param '\\{param\\}' \\(from \\{source\\}\\) contains an 'os\\.environ/' reference\\. Environment references in request-supplied parameters are no longer resolved server-side for security reasons\\.\\\\nTo resolve:\\\\n  1\\. Remove the 'os\\.environ/' reference from your request body / metadata\\.\\\\n  2\\. Either \\(a\\) configure this callback value in your proxy config\\.yaml under 'litellm_settings' / 'general_settings', or \\(b\\) pass the resolved secret value directly in the request\\.\\\\nSee https://docs\\.litellm\\.ai/docs/proxy/logging for server-side callback configuration\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/initialize_dynamic_callback_params.py","lineNumber":28,"sourceCode":"    kwargs: dict[str, Any],\n) -> Iterator[tuple[str, dict[str, Any]]]:\n    litellm_params: Final = kwargs.get(\"litellm_params\")\n    if isinstance(litellm_params, dict):\n        nested: Final = litellm_params.get(\"metadata\")\n        if isinstance(nested, dict):\n            yield \"litellm_params.metadata\", nested\n    for key in _CLIENT_CALLBACK_METADATA_SLOTS:\n        candidate = kwargs.get(key)\n        if isinstance(candidate, dict):\n            yield key, candidate\n\n\ndef _is_env_reference(value: object) -> bool:\n    return isinstance(value, str) and \"os.environ/\" in value\n\n\ndef _raise_env_reference_error(param: str, *, source: str) -> None:\n    raise ValueError(\n        f\"Callback param '{param}' (from {source}) contains an 'os.environ/' \"\n        \"reference. Environment references in request-supplied parameters are \"\n        \"no longer resolved server-side for security reasons.\\n\"\n        \"To resolve:\\n\"\n        \"  1. Remove the 'os.environ/' reference from your request body / \"\n        \"metadata.\\n\"\n        \"  2. Either (a) configure this callback value in your proxy \"\n        \"config.yaml under 'litellm_settings' / 'general_settings', or \"\n        \"(b) pass the resolved secret value directly in the request.\\n\"\n        \"See https://docs.litellm.ai/docs/proxy/logging for server-side \"\n        \"callback configuration.\"\n    )\n\n\ndef validate_no_callback_env_reference(param: str, value: object, *, source: str) -> None:\n    if _is_env_reference(value):\n        _raise_env_reference_error(param, source=source)\n","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/litellm_core_utils/initialize_dynamic_callback_params.py#L10-L46","documentation":"Security-hardening ValueError raised by initialize_standard_callback_dynamic_params(): request-supplied values for callback parameters (langfuse_secret, langsmith_api_key, slack_webhook_url, etc., whether top-level or inside metadata/litellm_params.metadata) must not contain the marker 'os.environ/'. Older LiteLLM resolved such references against the server's environment, letting any client read arbitrary server env vars, so the proxy now rejects them outright.","triggerScenarios":"POSTing to /chat/completions (or /v1/chat/completions) with extra_body/metadata like {'metadata': {'langfuse_secret': 'os.environ/LANGFUSE_SECRET'}} or a top-level 'langsmith_api_key': 'os.environ/LANGSMITH_API_KEY' — any supported callback param whose string value contains 'os.environ/'.","commonSituations":"Upgrading the LiteLLM proxy after the security fix while clients still send os.environ/ pointers that used to be resolved; copying config.yaml examples (where os.environ/ is valid) into request bodies; multi-tenant setups where each team passed env-var pointers for their observability keys.","solutions":["Remove the 'os.environ/' reference from the request body and send the literal, already-resolved secret value for the callback param.","Move the callback credential to server-side configuration: config.yaml litellm_settings/general_settings (env refs in config.yaml are still resolved at startup, on the trusted host).","If per-team callback creds are needed, use the proxy's team/key-level callback settings rather than raw request-body env pointers.","Rotate any secrets that were previously injectable via client requests."],"exampleFix":"# before\nclient.chat.completions.create(\n    model='gpt-4o', messages=messages,\n    extra_body={'metadata': {'langfuse_secret': 'os.environ/LANGFUSE_SECRET'}},\n)\n\n# after\nclient.chat.completions.create(\n    model='gpt-4o', messages=messages,\n    extra_body={'metadata': {'langfuse_secret': 'pk-lf-actual-value'}},\n)","handlingStrategy":"validation","validationCode":"def sanitize_callback_params(payload: dict) -> dict:\n    blocked = {'langfuse_public_key','langfuse_secret','langfuse_secret_key','langfuse_host','langsmith_api_key','slack_webhook_url','langfuse_prompt_version','arize_api_key','arize_space_key','arize_space_id','posthog_api_key','posthog_host','braintrust_api_key','braintrust_project','braintrust_host','lunary_public_key','humanloop_api_key','turn_off_message_logging','langsmith_base_url','langsmith_project','langsmith_sampling_rate','langsmith_tenant_id'}\n    meta = payload.setdefault('metadata', {})\n    for k in list(meta):\n        if k in blocked and isinstance(meta[k], str) and 'os.environ/' in meta[k]:\n            del meta[k]  # or substitute the resolved value client-side\n    for k in list(payload):\n        if k in blocked and isinstance(payload[k], str) and 'os.environ/' in payload[k]:\n            del payload[k]\n    return payload\n\npayload = sanitize_callback_params({'model': 'gpt-4o', 'messages': [...], 'metadata': {'langfuse_secret': 'os.environ/LANGFUSE_SECRET'}})\nresp = client.chat.completions.create(**payload)","typeGuard":"def is_env_reference(value: object) -> bool:\n    return isinstance(value, str) and 'os.environ/' in value","tryCatchPattern":"try:\n    resp = client.chat.completions.create(**payload)\nexcept openai.BadRequestError as e:\n    if 'os.environ/' in str(e) and 'Callback param' in str(e):\n        # strip env references from the request body and send resolved values instead\n        raise\n    raise","preventionTips":["Never put 'os.environ/' markers in request bodies; that syntax belongs to server-side config.yaml only.","Resolve secrets client-side (os.getenv) before adding them to metadata.","Configure shared callback credentials once in the proxy's litellm_settings/general_settings.","Add a client-side linter/test asserting no request payload contains 'os.environ/'."],"tags":["litellm","security","callbacks","env-vars","proxy","request-validation"],"backgroundTag":"env-secret-injection","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}