Mintplex-Labs/anything-llm · error · Error
Unauthorized. Please double check your AGENT_SERPLY_API_KEY
Error message
Unauthorized. Please double check your AGENT_SERPLY_API_KEY
What it means
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.
Source
Thrown at server/utils/agents/aibitat/plugins/web-browsing.js:778
const { response, error } = await fetch(url, {
method: "GET",
headers: {
"X-API-KEY": process.env.AGENT_SERPLY_API_KEY,
"Content-Type": "application/json",
"User-Agent": "anything-llm",
"X-Proxy-Location": proxy_location,
"X-User-Agent": device_type,
},
})
.then((res) => {
if (res.ok) return res.json();
throw new Error(
`${res.status} - ${res.statusText}. params: ${JSON.stringify({ auth: this.middleTruncate(process.env.AGENT_SERPLY_API_KEY, 5), q: query })}`
);
})
.then((data) => {
if (data?.message === "Unauthorized")
throw new Error(
"Unauthorized. Please double check your AGENT_SERPLY_API_KEY"
);
return { response: data, error: null };
})
.catch((e) => {
this.super.handlerProps.log(`Serply Error: ${e.message}`);
return { response: null, error: e.message };
});
if (error)
return `There was an error searching for content. ${error}`;
const data = [];
response.results?.forEach((searchResult) => {
const { title, link, description } = searchResult;
data.push({
title,
link,View on GitHub (pinned to 526360e320)
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'.
Example fix
// before
if (data?.message === "Unauthorized")
throw new Error("Unauthorized. Please double check your AGENT_SERLY_API_KEY");
// improved — surface the response body for diagnosis
if (data?.message === "Unauthorized") {
throw new Error(
`Serly rejected the API key. Verify AGENT_SERLY_API_KEY is valid and active. Response: ${JSON.stringify(data)}`
);
} Defensive patterns
Strategy: validation
Validate before calling
// Serly returns 200 OK with {message:'Unauthorized'} for invalid keys.
// Pre-validate the key format (length, no whitespace) before the request.
const key = process.env.AGENT_SERLY_API_KEY;
if (!key || key.trim().length < 10) {
throw new Error("AGENT_SERLY_API_KEY appears to be missing or too short.");
} Try / catch
try {
const results = await serlySearch(query);
return results;
} catch (e) {
if (e.message.includes("Unauthorized") && e.message.includes("SERLY")) {
return "Serly rejected the API key. Regenerate it from the Serly dashboard and update AGENT_SERLY_API_KEY.";
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- ${res.status} - ${res.statusText}. params: ${JSON.stringify(
- ${res.status} - ${res.statusText}. params: ${JSON.stringify(
- ${res.status} - ${res.statusText}. params: ${JSON.stringify(
- ${res.status} - ${res.statusText}. params: ${JSON.stringify(
- ${res.status} - ${res.statusText}. params: ${JSON.stringify(
AI-assisted analysis of Mintplex-Labs/anything-llm@526360e320 (2026-08-13).
Data as JSON: /api/errors/8c76fe9a3aa72247.
Report an issue: GitHub.