calesthio/OpenMontage · critical · KlingAPIError

KLING_API_KEY is not set. Configure KLING_API_KEY for offici

Error message

KLING_API_KEY is not set. Configure KLING_API_KEY for official Kling API access.

What it means

KlingAPIError (with http_status=401) raised lazily by the client's headers property whenever an authenticated request is attempted and self.api_key is falsy. The key is read once at construction from the KLING_API_KEY environment variable; if it wasn't set and no explicit api_key was passed, every request fails here with an actionable message naming the exact env var.

Source

Thrown at tools/_kling/client.py:43

class KlingClient:
    """Small synchronous client for the official Kling API."""

    def __init__(
        self,
        api_key: str | None = None,
        base_url: str | None = None,
        session: Any | None = None,
        max_retries: int = 2,
    ) -> None:
        self.api_key = api_key if api_key is not None else os.environ.get("KLING_API_KEY")
        self.base_url = (base_url or os.environ.get("KLING_API_BASE_URL") or DEFAULT_API_BASE_URL).rstrip("/")
        self.session = session or requests.Session()
        self.max_retries = max_retries

    @property
    def headers(self) -> dict[str, str]:
        if not self.api_key:
            raise KlingAPIError(
                "KLING_API_KEY is not set. Configure KLING_API_KEY for official Kling API access.",
                http_status=401,
            )
        return {
            "Authorization": f"Bearer {self.api_key}",
            "Accept": "application/json",
            "Content-Type": "application/json",
        }

    def post(self, path: str, payload: dict[str, Any]) -> dict[str, Any]:
        return self._request("post", path, json=payload)

    def get(self, path: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
        return self._request("get", path, params=params)

    def download(self, url: str, output_path: Path, timeout: int = 180) -> Path:
        output_path.parent.mkdir(parents=True, exist_ok=True)
        response = self.session.get(url, timeout=timeout)

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Export KLING_API_KEY in the environment where the process runs: export KLING_API_KEY=... (or add to .env + load it)
  2. Pass api_key explicitly to the KlingClient constructor when managing secrets programmatically (e.g. from a secrets manager)
  3. Verify with: python -c "import os; print(bool(os.environ.get('KLING_API_KEY')))"
  4. In CI, add KLING_API_KEY to the runner's secret store / masked variables

Example fix

# before
client = KlingClient()  # env var not set -> 401 KlingAPIError on first request

# after
client = KlingClient(api_key=os.environ["KLING_API_KEY"])  # fail fast at construction
Defensive patterns

Strategy: validation

Validate before calling

import os
if not os.environ.get("KLING_API_KEY"):
    raise SystemExit("KLING_API_KEY not set — export it before running Kling tools")
client = KlingClient()

Type guard

def kling_credentials_present() -> bool:
    return bool(os.environ.get("KLING_API_KEY"))

Try / catch

try:
    client.get("/v1/models/test")  # any cheap authenticated call
except KlingAPIError as e:
    if getattr(e, "http_status", None) == 401 and "KLING_API_KEY" in str(e):
        raise SystemExit("configure KLING_API_KEY (env var or KlingClient(api_key=...))")
    raise

Prevention

When it happens

Trigger: Constructing KlingClient in a shell/process where KLING_API_KEY was never exported; CI runners or agent sessions missing the secret; .env file present but not loaded; key passed as empty string (also falsy); subprocess spawning the client without inheriting the environment.

Common situations: New machine/container without the env var; direnv/pyenv activation missed; secret stored in .env but the process doesn't auto-load it; the key name typo'd (KLING_APIKEY / KLING_KEY).

Related errors


AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15). Data as JSON: /api/errors/98f6c8d8329217ee. Report an issue: GitHub.