{"record":{"id":"361d2311cf8046ad","repo":"tinyhumansai/openhuman","slug":"exa-request-failed-e","errorCode":null,"errorMessage":"Exa request failed: {e}","messagePattern":"Exa request failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/search/tools/exa.rs","lineNumber":226,"sourceCode":"            \"[exa] POST {url} (direct BYOK)\"\n        );\n\n        // Egress spine (privacy epic S2, #4436): disclose the destination before\n        // contacting Exa. `local_only_block` has already refused the call if the\n        // live policy forbids it.\n        crate::openhuman::security::egress::emit_external_transfer(self.egress_descriptor());\n\n        let resp = client\n            .post(&url)\n            .header(\"x-api-key\", api_key)\n            .header(\"Content-Type\", \"application/json\")\n            .header(\"Accept\", \"application/json\")\n            .json(&body)\n            .send()\n            .await\n            .map_err(|e| {\n                tracing::warn!(\"[exa] request to {path} failed: {e}\");\n                anyhow::anyhow!(\"Exa request failed: {e}\")\n            })?;\n\n        let status = resp.status();\n        if !status.is_success() {\n            // Read and drop the body: Exa echoes the query back in errors and\n            // the message reaches the agent transcript.\n            let body_len = resp.text().await.unwrap_or_default().len();\n            tracing::warn!(status = %status, body_len, \"[exa] non-2xx response\");\n            if status == reqwest::StatusCode::UNAUTHORIZED\n                || status == reqwest::StatusCode::FORBIDDEN\n            {\n                anyhow::bail!(\n                    \"Exa rejected the configured API key (HTTP {status}). \\\n                     Check your Exa API key under Connections > Search engine.\"\n                );\n            }\n            anyhow::bail!(\"Exa returned non-2xx status {status}\");\n        }","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/search/tools/exa.rs#L208-L244","documentation":"Raised in ExaClient::post when the reqwest POST to the Exa API fails at the transport layer — DNS failure, connection refused/reset, TLS handshake error, or timeout — before any HTTP status is obtained. The x-api-key header and JSON body were already attached, and the egress-spine disclosure (emit_external_transfer) has already run, so this error only covers 'the request never got a response'. It propagates to all Exa tool executions (search, find similar, contents).","triggerScenarios":"Thrown at src/openhuman/search/tools/exa.rs:226 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Check network connectivity and that https://api.exa.ai (or the configured api_url) is reachable.","Verify corporate proxy / firewall settings; the request honors the app's proxy-aware HTTP client config.","Retry — transient network failures are often short-lived; back off before repeated attempts.","If using a custom exa api_url in config, confirm the endpoint is correct and alive."],"exampleFix":null,"handlingStrategy":"retry","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"}