Mintplex-Labs/anything-llm · error · Error
${e.message}
Error message
${e.message} What it means
Re-thrown from the OpenAI SDK's promise rejection inside getChatCompletion: the `.catch((e) => { throw new Error(e.message); })` wrapper flattens whatever the Perplexity endpoint returned (auth failure, rate limit, malformed request, network timeout) into a plain Error carrying only the message string. The original status code / SDK error class (e.g. AuthenticationError, RateLimitError) is lost.
Source
Thrown at server/utils/AiProviders/perplexity/index.js:104
};
return [prompt, ...chatHistory, { role: "user", content: userPrompt }];
}
async getChatCompletion(messages = null, { temperature = 0.7 }) {
if (!(await this.isValidChatCompletionModel(this.model)))
throw new Error(
`Perplexity chat: ${this.model} is not valid for chat completion!`
);
const result = await LLMPerformanceMonitor.measureAsyncFunction(
this.openai.chat.completions
.create({
model: this.model,
messages,
temperature,
})
.catch((e) => {
throw new Error(e.message);
})
);
if (
!result.output.hasOwnProperty("choices") ||
result.output.choices.length === 0
)
return null;
return {
textResponse: result.output.choices[0].message.content,
metrics: {
prompt_tokens: result.output.usage?.prompt_tokens || 0,
completion_tokens: result.output.usage?.completion_tokens || 0,
total_tokens: result.output.usage?.total_tokens || 0,
outputTps: result.output.usage?.completion_tokens / result.duration,
duration: result.duration,
model: this.model,View on GitHub (pinned to 526360e320)
Solutions
- Read the message verbatim: '401' / 'Incorrect API key' -> rotate the key; '429' / 'rate limit' -> back off or upgrade plan; 'model not found' -> switch model.
- Reproduce with curl against https://api.perplexity.ai/chat/completions using the same key and model to confirm whether it is auth, quota, or the model id.
- Check Perplexity status/usage dashboard for outages or quota exhaustion.
- If transient (5xx / network), add a bounded retry with exponential backoff around getChatCompletion.
Example fix
// before
const text = (await llm.getChatCompletion(messages, { temperature: 0.7 })).textResponse;
// after
async function callWithRetry(llm, messages, opts, retries = 3) {
for (let attempt = 0; attempt <= retries; attempt++) {
try {
return await llm.getChatCompletion(messages, opts);
} catch (err) {
const transient = /429|5\d{2}|timeout|ECONNRESET|fetch failed/i.test(err.message);
if (!transient || attempt === retries) throw err;
await new Promise((r) => setTimeout(r, 2 ** attempt * 500));
}
}
}
const text = (await callWithRetry(llm, messages, { temperature: 0.7 })).textResponse; Defensive patterns
Strategy: retry
Validate before calling
function classifyProviderError(message) {
if (/401|unauthorized|invalid api key/i.test(message)) return "auth";
if (/429|rate limit|quota/i.test(message)) return "rate";
if (/5\d{2}|server error|timeout|ECONN/i.test(message)) return "transient";
return "fatal";
}
// decide before retrying whether the error is worth retrying
const kind = classifyProviderError(err.message); Type guard
function isTransientProviderError(message) {
return typeof message === "string" && /429|5\d{2}|timeout|ECONNRESET|fetch failed|socket hang up/i.test(message);
} Try / catch
async function perplexityCall(llm, messages, opts, retries = 3) {
for (let i = 0; i <= retries; i++) {
try {
return await llm.getChatCompletion(messages, opts);
} catch (e) {
if (/401|not valid for chat/i.test(e.message) || i === retries) throw e;
await new Promise((r) => setTimeout(r, 2 ** i * 500));
}
}
} Prevention
- Wrap every provider call in a classifier that distinguishes auth/rate/transient/fatal so retry policy is correct.
- Preserve the original SDK error by throwing `new Error(e.message, { cause: e })` so the status code is not lost.
- Set conservative concurrency limits and per-minute caps to avoid 429s.
- Monitor p95 latency and error rate per provider; alert on auth spikes indicating key rotation problems.
When it happens
Trigger: Perplexity's api.perplexity.ai returns non-2xx: 401 (bad/expired key), 403, 404 (unknown model on their side despite passing local validation), 429 (rate limit / quota), 5xx, or the OpenAI client throws a connection/timeout/abort error before a response arrives.
Common situations: PERPLEXITY_API_KEY was valid at construction but revoked later; exceeded the Perplexity plan's request rate; the chosen model is online-only and the account tier does not include it; flaky egress through a corporate proxy; the response stream was aborted by the client mid-request.
Related errors
AI-assisted analysis of Mintplex-Labs/anything-llm@526360e320 (2026-08-13).
Data as JSON: /api/errors/8189e61d142ba62e.
Report an issue: GitHub.