{"record":{"id":"5d3f993eb575ed20","repo":"bitwarden/server","slug":"haveibeenpwned-api-key-not-set","errorCode":null,"errorMessage":"HaveIBeenPwned API key not set.","messagePattern":"HaveIBeenPwned API key not set\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Dirt/Controllers/HibpController.cs","lineNumber":55,"sourceCode":"        GlobalSettings globalSettings)\n    {\n        _userService = userService;\n        _currentContext = currentContext;\n        _globalSettings = globalSettings;\n        _userAgent = _globalSettings.SelfHosted ? \"Bitwarden Self-Hosted\" : \"Bitwarden\";\n    }\n\n    [HttpGet(\"breach\")]\n    public async Task<IActionResult> Get(string username)\n    {\n        return await SendAsync(WebUtility.UrlEncode(username), true);\n    }\n\n    private async Task<IActionResult> SendAsync(string username, bool retry)\n    {\n        if (!CoreHelpers.SettingHasValue(_globalSettings.HibpApiKey))\n        {\n            throw new BadRequestException(\"HaveIBeenPwned API key not set.\");\n        }\n        var request = new HttpRequestMessage(HttpMethod.Get, string.Format(HibpBreachApi, username));\n        request.Headers.Add(\"hibp-api-key\", _globalSettings.HibpApiKey);\n        request.Headers.Add(\"hibp-client-id\", GetClientId());\n        request.Headers.Add(\"User-Agent\", _userAgent);\n        var response = await _httpClient.SendAsync(request);\n        if (response.IsSuccessStatusCode)\n        {\n            var data = await response.Content.ReadAsStringAsync();\n            return Content(data, \"application/json\");\n        }\n        else if (response.StatusCode == HttpStatusCode.NotFound)\n        {\n            /* 12/1/2025 - Per the HIBP API, If the domain does not have any email addresses in any breaches, \n               an HTTP 404 response will be returned. API also specifies that \"404 Not found is the account could \n               not be found and has therefore not been pwned\". Per REST semantics we will return 200 OK with empty array. */\n            return Content(\"[]\", \"application/json\");\n        }","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Dirt/Controllers/HibpController.cs#L37-L73","documentation":"Thrown as BadRequestException by the HaveIBeenPwned breach endpoint when GlobalSettings.HibpApiKey has no value. The controller refuses to proxy any HIBP request without a configured key because the upstream API requires one; SettingHasValue returns false for null/empty/whitespace.","triggerScenarios":"Any GET /hibp/breach?username=... call when HibpApiKey is unset/empty/whitespace in the server's global settings (environment/config). It is a config error surfaced to the caller as 400.","commonSituations":"New self-hosted deployment that never configured the HIBP key; key removed from environment in an upgrade; key set on the wrong environment variable name; cloud-to-self-host migration missing the key.","solutions":["Set the HibpApiKey global setting (environment variable / appsettings) to a valid HIBP API key.","Restart/reload the app so the config is picked up.","Obtain a key from HaveIBeenPwned if one is not held.","If HIBP is intentionally disabled, prevent the client from calling the breach endpoint."],"exampleFix":"// before: no key configured\n//   globalSettings:HibpApiKey = null  -> GET /hibp/breach -> 400\n//\n// after: set the environment variable and restart\n//   HibpApiKey__value is configured in appsettings or env:\nexport globalSettings__hibpApiKey=\"<your-hibp-key>\"\n# then restart the API process","handlingStrategy":"validation","validationCode":"// Verify the HIBP key is configured before exposing the breach endpoint to users\nconst hasKey = settingHasValue(globalSettings.hibpApiKey);\nif (!hasKey) {\n  // disable the breach-check UI or show 'feature unavailable'\n  return unavailable();\n}\nawait getBreaches(username);","typeGuard":"function settingHasValue(v) { return typeof v === 'string' && v.trim().length > 0; }","tryCatchPattern":"try {\n  await getBreaches(username);\n} catch (e) {\n  if (e.status === 400 && /hibp.*api key/i.test(e.message)) {\n    // surface a server-config error to operators, not end users\n    notifyOpsToSetHibpKey();\n  } else throw e;\n}","preventionTips":["Set HibpApiKey in global settings (appsettings/env) on every environment that exposes breach checks.","Restart the API after changing config.","Hide the breach-check UI when the key is unset to avoid surfacing 400s to end users."],"tags":["hibp","configuration","self-hosted","http-400","breach-reports"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}