BerriAI/litellm · critical · GraySwanGuardrailMissingSecrets

Gray Swan API key missing. Set `GRAYSWAN_API_KEY` or pass `a

Error message

Gray Swan API key missing. Set `GRAYSWAN_API_KEY` or pass `api_key`.

What it means

GraySwanGuardrailMissingSecrets raised in GraySwanGuardrail.__init__ when both the api_key parameter and the GRAYSWAN_API_KEY environment variable are empty. It is a dedicated exception (not plain ValueError) signaling missing credentials at guardrail construction time, before any monitor API call is attempted.

Source

Thrown at litellm/proxy/guardrails/guardrail_hooks/grayswan/grayswan.py:84

        guardrail_name: str | None = "grayswan",
        api_key: str | None = None,
        api_base: str | None = None,
        on_flagged_action: str | None = None,
        violation_threshold: float | None = None,
        reasoning_mode: str | None = None,
        categories: dict[str, str] | None = None,
        policy_id: str | None = None,
        streaming_end_of_stream_only: bool = False,
        streaming_sampling_rate: int = 5,
        fail_open: bool | None = True,
        guardrail_timeout: float | None = 30.0,
        **kwargs: Any,
    ) -> None:
        self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback)

        api_key_value: Final = api_key or os.getenv("GRAYSWAN_API_KEY")
        if not api_key_value:
            raise GraySwanGuardrailMissingSecrets(
                "Gray Swan API key missing. Set `GRAYSWAN_API_KEY` or pass `api_key`."
            )
        self.api_key: str = api_key_value

        base: Final = api_base or os.getenv("GRAYSWAN_API_BASE") or self.BASE_API_URL
        self.api_base = base.rstrip("/")
        self.monitor_url = f"{self.api_base}{self.MONITOR_PATH}"

        action: Final = on_flagged_action
        if action and action.lower() in self.SUPPORTED_ON_FLAGGED_ACTIONS:
            self.on_flagged_action = action.lower()
        else:
            if action:
                verbose_proxy_logger.warning(
                    "Gray Swan Guardrail: Unsupported on_flagged_action '%s', defaulting to '%s'.",
                    action,
                    self.DEFAULT_ON_FLAGGED_ACTION,
                )

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Set GRAYSWAN_API_KEY in the proxy's runtime environment.
  2. Or pass api_key: os.environ/GRAYSWAN_API_KEY in the guardrail's litellm_params.
  3. Optionally set GRAYSWAN_API_BASE if your deployment uses a non-default base URL.
  4. Restart the proxy after injecting the secret.

Example fix

# before
litellm_params:
  guardrail: grayswan
  on_flagged_action: block

# after
litellm_params:
  guardrail: grayswan
  on_flagged_action: block
  api_key: os.environ/GRAYSWAN_API_KEY
Defensive patterns

Strategy: validation

Validate before calling

import os

if not (os.getenv('GRAYSWAN_API_KEY') or cfg.litellm_params.get('api_key')):
    raise SystemExit('GRAYSWAN_API_KEY not set — refusing to start with grayswan guardrail')

Type guard

from litellm.proxy.guardrails.guardrail_hooks.grayswan.grayswan import GraySwanGuardrailMissingSecrets

def is_grayswan_missing_secrets(exc: BaseException) -> bool:
    return isinstance(exc, GraySwanGuardrailMissingSecrets)

Try / catch

from litellm.proxy.guardrails.guardrail_hooks.grayswan.grayswan import GraySwanGuardrailMissingSecrets
try:
    guardrail = GraySwanGuardrail(litellm_params=cfg)
except GraySwanGuardrailMissingSecrets as e:
    logger.error('deploy misconfigured: %s', e)
    raise

Prevention

When it happens

Trigger: A grayswan guardrail entry without api_key in litellm_params while GRAYSWAN_API_KEY is unset in the proxy process; secrets not mounted into the container/pod running the proxy.

Common situations: Local dev works (env var in shell) but the deployed proxy lacks the secret; secret rotated and the new value not propagated; docker-compose/K8s secret reference typo'd.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/cdaa2d4b1400d12f. Report an issue: GitHub.