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

  1. Read the message verbatim: '401' / 'Incorrect API key' -> rotate the key; '429' / 'rate limit' -> back off or upgrade plan; 'model not found' -> switch model.
  2. 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.
  3. Check Perplexity status/usage dashboard for outages or quota exhaustion.
  4. 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

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.