{"record":{"id":"29261f566c0c50fd","repo":"HKUDS/DeepTutor","slug":"azure-openai-api-key-is-required","errorCode":null,"errorMessage":"Azure OpenAI api_key is required","messagePattern":"Azure OpenAI api_key is required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/llm/provider_core/azure_openai_provider.py","lineNumber":84,"sourceCode":"\n\nclass AzureOpenAIProvider(LLMProvider):\n    \"\"\"Azure OpenAI provider using the Responses API.\"\"\"\n\n    def __init__(\n        self,\n        api_key: str = \"\",\n        api_base: str = \"\",\n        default_model: str = \"gpt-5.2-chat\",\n        extra_headers: dict[str, str] | None = None,\n        api_version: str | None = None,\n    ):\n        super().__init__(api_key, api_base)\n        self.default_model = default_model\n        self.extra_headers = extra_headers or {}\n\n        if not api_key:\n            raise ValueError(\"Azure OpenAI api_key is required\")\n        if not api_base:\n            raise ValueError(\"Azure OpenAI api_base is required\")\n\n        base_url = normalize_azure_base_url(api_base)\n\n        # Azure authenticates API keys through ``api-key``; the SDK only sends\n        # ``Authorization: Bearer``, which the service reserves for Entra tokens.\n        headers = {\"x-session-affinity\": uuid.uuid4().hex, \"api-key\": api_key}\n        if extra_headers:\n            headers.update(extra_headers)\n\n        # The ``/openai/v1`` surface supersedes ``?api-version=``, so a classic\n        # dated version configured for the probe's URL would be rejected here.\n        # Only ``preview`` is forwarded, since Azure still gates preview-only\n        # Responses features behind it.\n        default_query = (\n            {\"api-version\": \"preview\"} if (api_version or \"\").strip().lower() == \"preview\" else None\n        )","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/llm/provider_core/azure_openai_provider.py#L66-L102","documentation":"AzureOpenAIProvider.__init__ requires a non-empty api_key because Azure OpenAI rejects anonymous requests — unlike some local endpoints, there is no keyless mode.","triggerScenarios":"Constructing AzureOpenAIProvider with api_key=None/'' — e.g. the catalog/settings entry for the Azure provider lacks the key, or the env var it maps from is unset.","commonSituations":"AZURE_OPENAI_API_KEY missing from the environment; provider spec created from a template without filling the key; key stored under the wrong settings key name.","solutions":["Set the Azure OpenAI API key in Settings > Catalog (or the mapped env var) and re-instantiate the provider.","Confirm you're using an Azure key, not an OpenAI platform sk- key.","Check that config loading isn't silently dropping the key field."],"exampleFix":"# before\nprov = AzureOpenAIProvider(api_key=None, api_base='https://x.openai.azure.com/')\n# after\nprov = AzureOpenAIProvider(api_key=os.environ['AZURE_OPENAI_API_KEY'], api_base='https://x.openai.azure.com/')","handlingStrategy":"validation","validationCode":"import os\n\ndef azure_key_present() -> bool:\n    return bool(os.getenv('AZURE_OPENAI_API_KEY'))","typeGuard":null,"tryCatchPattern":"try:\n    prov = AzureOpenAIProvider(api_key=key, api_base=base)\nexcept ValueError as e:\n    if 'api_key' in str(e):\n        key = os.environ['AZURE_OPENAI_API_KEY']; prov = AzureOpenAIProvider(key, base)\n    else:\n        raise","preventionTips":["Validate required provider fields at app startup, not first API call","Store Azure keys in a secrets manager, not loose JSON","Distinguish Azure keys from OpenAI keys in catalog naming"],"tags":["config","azure","missing-api-key"],"backgroundTag":"missing-api-key","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}