{"record":{"id":"7132bdcfefba5fe3","repo":"headroomlabs-ai/headroom","slug":"token-url-is-required","errorCode":null,"errorMessage":"token_url is required","messagePattern":"token_url is required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"plugins/headroom-oauth2/src/headroom_oauth2/provider.py","lineNumber":58,"sourceCode":"    \"\"\"\n\n    def __init__(\n        self,\n        *,\n        token_url: str,\n        client_id: str,\n        client_secret: str,\n        scopes=None,\n        audience: str | None = None,\n        grant_type: str = \"client_credentials\",\n        auth_style: str = \"post\",\n        extra_params=None,\n        skew_seconds: int = 60,\n        timeout_seconds: float = 30.0,\n        allow_insecure: bool = False,\n    ):\n        if not token_url:\n            raise ValueError(\"token_url is required\")\n        if not client_id or not client_secret:\n            raise ValueError(\"client_id and client_secret are required\")\n        if auth_style not in (\"post\", \"basic\"):\n            raise ValueError(\"auth_style must be 'post' or 'basic'\")\n        if not allow_insecure and not _https_or_local(token_url):\n            raise ValueError(\n                \"token_url must be https (loopback http allowed for tests; set \"\n                \"allow_insecure=True / HEADROOM_OAUTH2_ALLOW_INSECURE=1 to override)\"\n            )\n        self.token_url = token_url\n        self.client_id = client_id\n        self.client_secret = client_secret\n        self.scopes = list(scopes or [])\n        self.audience = audience\n        self.grant_type = grant_type\n        self.auth_style = auth_style\n        self.extra_params = dict(extra_params or {})\n        self.skew = max(0, int(skew_seconds))","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/plugins/headroom-oauth2/src/headroom_oauth2/provider.py#L40-L76","documentation":"OAuth2ClientCredentials validates at construction that `token_url` is a non-empty string; an empty or None token_url raises ValueError immediately. token_url is the one setting with no default because the provider cannot mint tokens without knowing the IdP endpoint.","triggerScenarios":"Constructing `OAuth2ClientCredentials(token_url=\"\", ...)` or `token_url=None` (e.g. from a config object whose field was never populated, or from `provider_from_env` logic where the env var was empty).","commonSituations":"Config dataclasses instantiated with placeholder values; env-var plumbing where HEADROOM_OAUTH2_TOKEN_URL is set but exports as empty string in some environments; tests constructing the provider directly without a URL; config files split per environment and the token URL key misspelled so it reads as None.","solutions":["Supply a real token endpoint URL, e.g. `token_url=\"https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token\"`","If building from env, guard first: only construct the provider when `os.environ.get(\"HEADROOM_OAUTH2_TOKEN_URL\")` is truthy — empty means 'disabled' and you should skip construction","Check for typos/misspelled keys in the config mapping that feeds token_url so it isn't silently None"],"exampleFix":"# before\nprovider = OAuth2ClientCredentials(token_url=cfg.get(\"token_url\"), client_id=..., client_secret=...)\n\n# after\nurl = cfg.get(\"token_url\")\nif not url:\n    raise RuntimeError(\"oauth2 enabled but token_url missing in config\")\nprovider = OAuth2ClientCredentials(token_url=url, client_id=..., client_secret=...)","handlingStrategy":"validation","validationCode":"def build_provider(cfg) -> OAuth2ClientCredentials | None:\n    url = (cfg.get(\"token_url\") or \"\").strip()\n    if not url:\n        if cfg.get(\"oauth2_enabled\"):\n            raise RuntimeError(\"oauth2 enabled but token_url is empty\")\n        return None  # feature off\n    return OAuth2ClientCredentials(token_url=url, ...)\n\nprovider = build_provider(cfg)","typeGuard":"def has_token_url(cfg: dict) -> bool:\n    return bool(str(cfg.get(\"token_url\") or \"\").strip())","tryCatchPattern":"try:\n    provider = OAuth2ClientCredentials(token_url=url, ...)\nexcept ValueError as e:\n    if \"token_url is required\" in str(e):\n        raise RuntimeError(\"config error: token_url missing — disable oauth2 or set it\") from e\n    raise","preventionTips":["Make token_url required in your typed config (pydantic) so it fails at config load","Treat empty-string env values as unset: strip before checking truthiness","Add a smoke test that constructs the provider from real env in staging"],"tags":["oauth2","validation","configuration","precondition"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}