Mintplex-Labs/anything-llm · error

Voyage AI failed to embed: Rate limit reached

Error message

Voyage AI failed to embed: Rate limit reached

What it means

The @langchain VoyageEmbeddings client does not surface Voyage API errors cleanly: when the API rejects the request (most often rate limiting), the client throws the TypeError "Cannot read properties of undefined (reading '0')" while reading the response. VoyageAiEmbedder catches that specific TypeError and rethrows it as an explicit rate-limit error, because an undefined first result almost always means the API returned an error body instead of embeddings.

Solutions

  1. Wait for the rate-limit window to reset and retry — the call is idempotent for the same input.
  2. Reduce pressure: upload fewer/smaller documents at once, or pause other jobs sharing the key.
  3. Check the Voyage AI dashboard for quota usage and upgrade the tier if this recurs.
  4. Verify VOYAGEAI_API_KEY is valid — auth failures can produce the same underlying TypeError.

Example fix

// before — fire and forget
const vectors = await embedder.embedChunks(chunks);

// after — retry with backoff on the rate-limit error
async function embedWithRetry(embedder, chunks, attempts = 5) {
  for (let i = 1; ; i++) {
    try {
      return await embedder.embedChunks(chunks);
    } catch (err) {
      if (err.message.includes("Rate limit reached") && i < attempts) {
        await new Promise((r) => setTimeout(r, i * 5000));
        continue;
      }
      throw err;
    }
  }
}
Defensive patterns

Strategy: retry

Validate before calling

function estimateVoyageBatches(chunkCount, batchSize = 128) {
  return Math.ceil(chunkCount / batchSize);
}
// Before embedding a big document, check you stay inside your plan's RPM:
if (estimateVoyageBatches(chunks.length) > myPlansRequestsPerMinute) {
  throw new Error(
    "This upload would exceed the Voyage AI rate limit — split it into smaller jobs."
  );
}

Try / catch

async function embedVoyageWithBackoff(embedder, chunks, maxAttempts = 5) {
  for (let i = 1; ; i++) {
    try {
      return await embedder.embedChunks(chunks);
    } catch (err) {
      if (err.message.includes("Rate limit reached") && i < maxAttempts) {
        await new Promise((r) => setTimeout(r, i * 5_000));
        continue;
      }
      throw err;
    }
  }
}

Prevention

When it happens

Trigger: Exceeding Voyage AI RPM/TPM limits while embedDocuments() fans textChunks out into batches of 128; free-tier keys embedding a large document; concurrent embedding jobs sharing one key; an invalid/revoked key taking the same undefined-response path.

Common situations: Initial ingestion of a big workspace on voyage-3-lite free tier; parallel document uploads multiplying request count above the plan limit.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18). Data as JSON: /api/errors/66abed9bbf5f8114. Report an issue: GitHub.

Appendix: source

Thrown at server/utils/EmbeddingEngines/voyageAi/index.js:63

    );

    // If given an array return the native Array[Array] format since that should be the outcome.
    // But if given a single string, we need to flatten it so that we have a 1D array.
    return (Array.isArray(textInput) ? result : result.flat()) || [];
  }

  async embedChunks(textChunks = []) {
    try {
      const embeddings = await this.voyage.embedDocuments(textChunks);
      return embeddings;
    } catch (error) {
      console.error("Voyage AI Failed to embed:", error);
      if (
        error.message.includes(
          "Cannot read properties of undefined (reading '0')"
        )
      )
        throw new Error("Voyage AI failed to embed: Rate limit reached");
      throw error;
    }
  }
}

module.exports = {
  VoyageAiEmbedder,
};

View on GitHub (pinned to 3aec848f28)