{"record":{"id":"68d20b0c1040b329","repo":"tinyhumansai/openhuman","slug":"exa-search-unavailable-no-api-key-configured-add","errorCode":null,"errorMessage":"Exa search unavailable: no API key configured. Add your Exa API key under Connections > Search engine, set EXA_API_KEY or OPENHUMAN_EXA_API_KEY, or add search.exa.api_key to config.toml.","messagePattern":"Exa search unavailable: no API key configured\\. Add your Exa API key under Connections > Search engine, set EXA_API_KEY or OPENHUMAN_EXA_API_KEY, or add search\\.exa\\.api_key to config\\.toml\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/search/tools/exa.rs","lineNumber":179,"sourceCode":"    }\n\n    /// Privacy epic S7 (#4441): under `LocalOnly` the search is refused before\n    /// anything reaches Exa. Returns the `[policy-blocked]` tool result to hand\n    /// straight back from `execute`, or `None` when the transfer is permitted.\n    fn local_only_block(&self) -> Option<ToolResult> {\n        crate::openhuman::security::egress::local_only_tool_block(&self.egress_descriptor())\n            .map(ToolResult::error)\n    }\n\n    /// The configured key, or a user-actionable error naming exactly where to\n    /// set it. Surfaced verbatim on the first search attempt.\n    fn key(&self) -> anyhow::Result<&str> {\n        self.api_key\n            .as_deref()\n            .map(str::trim)\n            .filter(|k| !k.is_empty())\n            .ok_or_else(|| {\n                anyhow::anyhow!(\n                    \"Exa search unavailable: no API key configured. Add your Exa API key \\\n                     under Connections > Search engine, set EXA_API_KEY or \\\n                     OPENHUMAN_EXA_API_KEY, or add search.exa.api_key to config.toml.\"\n                )\n            })\n    }\n\n    /// Requested result count, honouring both `max_results` and Exa's native\n    /// `numResults` spelling. An explicit per-call value is clamped to the\n    /// API's own 1..=20 range rather than to the configured `max_results` --\n    /// config supplies the *default* when the call omits one, and a caller may\n    /// ask for more (this matches `querit.rs`).\n    fn requested_results(&self, args: &Value) -> usize {\n        args.get(\"max_results\")\n            .or_else(|| args.get(\"num_results\"))\n            .or_else(|| args.get(\"numResults\"))\n            .and_then(Value::as_u64)\n            .map(|n| n.clamp(1, 20) as usize)","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/search/tools/exa.rs#L161-L197","documentation":"Error \"Exa search unavailable: no API key configured. Add your Exa API key under Connections > Search engine, set EXA_API_KEY or OPENHUMAN_EXA_API_KEY, or add search.exa.api_key to config.toml.\" thrown in tinyhumansai/openhuman.","triggerScenarios":"Thrown at src/openhuman/search/tools/exa.rs:179 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Add the Exa API key in the app: Connections > Search engine.","Set the EXA_API_KEY or OPENHUMAN_EXA_API_KEY environment variable.","Add search.exa.api_key to config.toml and restart the core.","Fall back to another configured search provider (Brave, Parallel) if immediate Exa setup is not possible."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}