{"record":{"id":"8c76fe9a3aa72247","repo":"Mintplex-Labs/anything-llm","slug":"unauthorized-please-double-check-your-agent-serpl","errorCode":null,"errorMessage":"Unauthorized. Please double check your AGENT_SERPLY_API_KEY","messagePattern":"Unauthorized\\. Please double check your AGENT_SERPLY_API_KEY","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/utils/agents/aibitat/plugins/web-browsing.js","lineNumber":778,"sourceCode":"            const { response, error } = await fetch(url, {\n              method: \"GET\",\n              headers: {\n                \"X-API-KEY\": process.env.AGENT_SERPLY_API_KEY,\n                \"Content-Type\": \"application/json\",\n                \"User-Agent\": \"anything-llm\",\n                \"X-Proxy-Location\": proxy_location,\n                \"X-User-Agent\": device_type,\n              },\n            })\n              .then((res) => {\n                if (res.ok) return res.json();\n                throw new Error(\n                  `${res.status} - ${res.statusText}. params: ${JSON.stringify({ auth: this.middleTruncate(process.env.AGENT_SERPLY_API_KEY, 5), q: query })}`\n                );\n              })\n              .then((data) => {\n                if (data?.message === \"Unauthorized\")\n                  throw new Error(\n                    \"Unauthorized. Please double check your AGENT_SERPLY_API_KEY\"\n                  );\n                return { response: data, error: null };\n              })\n              .catch((e) => {\n                this.super.handlerProps.log(`Serply Error: ${e.message}`);\n                return { response: null, error: e.message };\n              });\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, link, description } = searchResult;\n              data.push({\n                title,\n                link,","sourceCodeStart":760,"sourceCodeEnd":796,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/526360e320da9d1b36074be5ed64fe76e5bbfbbd/server/utils/agents/aibitat/plugins/web-browsing.js#L760-L796","documentation":"Thrown by the Serly provider specifically when the HTTP response is 200 OK (so it passed the res.ok check) but the JSON body contains {message: 'Unauthorized'}. Serly uses this non-standard pattern instead of a proper 401 status. This is a second-layer auth check after the HTTP status check passes.","triggerScenarios":"AGENT_SERLY_API_KEY is set but invalid, revoked, or belongs to a deactivated account. Serly accepts the request at the HTTP layer (200) but rejects the key at the application layer, returning the Unauthorized message in the body. This cannot be caught by HTTP status alone.","commonSituations":"Key was rotated but the old one is still in the environment; key from a test account used in production; account suspended but API still responding 200; typo in the key that happens to be syntactically plausible.","solutions":["Generate a new key from the Serly dashboard and update AGENT_SERLY_API_KEY.","Verify the account associated with the key is active and in good standing.","Confirm the key has no leading/trailing whitespace (common when copied from a dashboard).","Test the key directly: curl -H 'X-API-KEY: <key>' 'https://api.serply.io/v1/search/q=test'."],"exampleFix":"// before\nif (data?.message === \"Unauthorized\")\n  throw new Error(\"Unauthorized. Please double check your AGENT_SERLY_API_KEY\");\n\n// improved — surface the response body for diagnosis\nif (data?.message === \"Unauthorized\") {\n  throw new Error(\n    `Serly rejected the API key. Verify AGENT_SERLY_API_KEY is valid and active. Response: ${JSON.stringify(data)}`\n  );\n}","handlingStrategy":"validation","validationCode":"// Serly returns 200 OK with {message:'Unauthorized'} for invalid keys.\n// Pre-validate the key format (length, no whitespace) before the request.\nconst key = process.env.AGENT_SERLY_API_KEY;\nif (!key || key.trim().length < 10) {\n  throw new Error(\"AGENT_SERLY_API_KEY appears to be missing or too short.\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  const results = await serlySearch(query);\n  return results;\n} catch (e) {\n  if (e.message.includes(\"Unauthorized\") && e.message.includes(\"SERLY\")) {\n    return \"Serly rejected the API key. Regenerate it from the Serly dashboard and update AGENT_SERLY_API_KEY.\";\n  }\n  throw e;\n}","preventionTips":["Remember Serly's non-standard 200-OK-but-unauthorized behavior — always check the response body message field.","Rotate keys from the Serly dashboard if this error persists.","Trim whitespace when setting the key in environment variables.","Test the key with a direct curl before relying on the agent."],"tags":["serly","web-search","authentication","api-key","agent-tool","non-standard-http"],"backgroundTag":null,"analyzedSha":"526360e320da9d1b36074be5ed64fe76e5bbfbbd","analyzedAt":"2026-08-13T01:45:47.170Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}