{"record":{"id":"c7e9c704901d9372","repo":"Mintplex-Labs/anything-llm","slug":"res-status-res-statustext","errorCode":null,"errorMessage":"${res.status} - ${res.statusText}","messagePattern":"\\$\\{res\\.status\\} - \\$\\{res\\.statusText\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/utils/agents/aibitat/plugins/web-browsing.js","lineNumber":1402,"sourceCode":"            const headers = {\n              \"Content-Type\": \"application/json\",\n              \"User-Agent\": \"keenable-anythingllm\",\n              \"X-Keenable-Title\": getAnythingLLMUserAgent(),\n            };\n\n            // Keyless public endpoint by default; keyed endpoint + X-API-Key\n            // when a key is configured.\n            const path = apiKey ? \"/v1/search\" : \"/v1/search/public\";\n            if (apiKey) headers[\"X-API-Key\"] = apiKey;\n\n            const { response, error } = await fetch(`${baseUrl}${path}`, {\n              method: \"POST\",\n              headers,\n              body: JSON.stringify({ query: String(query), mode: \"pro\" }),\n            })\n              .then((res) => {\n                if (res.ok) return res.json();\n                throw new Error(`${res.status} - ${res.statusText}`);\n              })\n              .then((data) => {\n                return { response: data, error: null };\n              })\n              .catch((e) => {\n                this.super.handlerProps.log(\n                  `Keenable Search Error: ${e.message}`\n                );\n                return { response: null, error: e.message };\n              });\n            if (error)\n              return `There was an error searching for content. ${error}`;\n\n            const data = [];\n            response.results?.forEach((searchResult) => {\n              const { title, url, description, snippet } = searchResult;\n              // Keenable returns both fields: `snippet` carries the page text and\n              // `description` is the page's meta description, which is empty for","sourceCodeStart":1384,"sourceCodeEnd":1420,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/a145d4d87d086bdb31d50f9bf9cd9c46d311780c/server/utils/agents/aibitat/plugins/web-browsing.js#L1384-L1420","documentation":"After POSTing the search query to the Keenable endpoint, the plugin checks res.ok; any non-2xx HTTP response causes it to throw an Error formatted as '<status> - <statusText>' (e.g. '401 - Unauthorized'). The thrown message is caught locally and returned to the agent as 'There was an error searching for content. <status> - <statusText>'. It is a thin wrapper that surfaces the upstream Keenable API's rejection of the request.","triggerScenarios":"The fetch to `${baseUrl}${path}` (either /v1/search with X-API-Key when AGENT_KEENABLE_API_KEY is set, or /v1/search/public) returns a non-2xx status: 401/403 for an invalid or missing API key on the keyed endpoint, 404 when AGENT_KEENABLE_API_URL points to a host without the search route, 429 on rate limiting, or 5xx from an outage/misconfigured proxy.","commonSituations":"Expired or wrong AGENT_KEENABLE_API_KEY, self-hosted proxy at AGENT_KEENABLE_API_URL that doesn't implement /v1/search or /v1/search/public, rate-limit throttling under heavy agent traffic, or Keenable service downtime returning 502/503.","solutions":["Check the numeric status in the message: 401/403 means fix AGENT_KEENABLE_API_KEY (correct, non-expired key for the /v1/search endpoint); 404 means AGENT_KEENABLE_API_URL points to a host that doesn't serve the Keenable search routes; 429 means back off/retry later; 5xx means the upstream service is down.","If you don't need the keyed endpoint, unset AGENT_KEENABLE_API_KEY so the plugin falls back to the keyless /v1/search/public route.","Curl the endpoint directly to reproduce: POST {baseUrl}/v1/search (or /v1/search/public) with the same headers and body {\"query\":\"test\",\"mode\":\"pro\"} and inspect the response body for details the wrapper discards.","If running a self-hosted proxy, verify it proxies POST /v1/search and /v1/search/public to Keenable unchanged, including the X-API-Key header.","Retry the search after addressing the cause — the plugin already catches the throw and degrades to an error string, so no crash occurs."],"exampleFix":"// before (diagnose the raw status)\nthrow new Error(`${res.status} - ${res.statusText}`);\n// after (surface upstream detail)\nconst body = await res.text();\nthrow new Error(`${res.status} - ${res.statusText}: ${body.slice(0, 200)}`);","handlingStrategy":"retry","validationCode":"async function keenableReachable(baseUrl, apiKey) {\n  const path = apiKey ? \"/v1/search\" : \"/v1/search/public\";\n  const headers = { \"Content-Type\": \"application/json\" };\n  if (apiKey) headers[\"X-API-Key\"] = apiKey;\n  const res = await fetch(`${baseUrl}${path}`, {\n    method: \"POST\",\n    headers,\n    body: JSON.stringify({ query: \"ping\", mode: \"pro\" }),\n  });\n  if (!res.ok) throw new Error(`Keenable endpoint unhealthy: ${res.status} - ${res.statusText}`);\n  return true;\n}","typeGuard":"function isOkResponse(res) {\n  return res instanceof Response && res.ok && res.status >= 200 && res.status < 300;\n}","tryCatchPattern":"const MAX_RETRIES = 3;\nfor (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n  try {\n    return await agent._keenableSearch(query);\n  } catch (e) {\n    const status = parseInt(e.message, 10);\n    const retryable = status === 429 || status >= 500;\n    if (!retryable || attempt === MAX_RETRIES) {\n      console.error(`Keenable search failed: ${e.message}`);\n      return null;\n    }\n    await new Promise((r) => setTimeout(r, 2 ** attempt * 500));\n  }\n}","preventionTips":["Verify AGENT_KEENABLE_API_KEY is valid and present whenever you expect the keyed /v1/search endpoint to be used.","Smoke-test the configured AGENT_KEENABLE_API_URL host with curl before deploying; confirm it implements the search route.","Log and alert on the status code (401/403 = credentials, 404 = wrong base URL, 429 = throttle, 5xx = outage) instead of treating all failures alike.","Back off and retry on 429/5xx; fail fast on 4xx client errors.","Watch Keenable status/uptime to anticipate 5xx outages."],"tags":["http","api","search","upstream"],"backgroundTag":"http-error-response","analyzedSha":"a145d4d87d086bdb31d50f9bf9cd9c46d311780c","analyzedAt":"2026-09-15T14:32:06.641Z","contentChangedAt":"2026-09-15T14:32:06.641Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}