fix: prevent indexer hanging on OpenAI insufficient quota errors (#5350)

This commit is contained in:
hannesrudolph 2025-07-02 13:23:35 -06:00 committed by Daniel Riccio
parent a83e8c0553
commit f2255d900d
No known key found for this signature in database
GPG key ID: FFD5FD825F8E8209
22 changed files with 188 additions and 2 deletions

View file

@ -3,8 +3,10 @@
"authenticationFailed": "No s'han pogut crear les incrustacions: ha fallat l'autenticació. Comproveu la vostra clau d'API.",
"failedWithStatus": "No s'han pogut crear les incrustacions després de {{attempts}} intents: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "No s'han pogut crear les incrustacions després de {{attempts}} intents: {{errorMessage}}",
"insufficientQuota": "No s'han pogut crear les incrustacions: Quota insuficient. Comproveu el saldo del vostre compte OpenAI i afegiu crèdits per continuar.",
"failedMaxAttempts": "No s'han pogut crear les incrustacions després de {{attempts}} intents",
"textExceedsTokenLimit": "El text a l'índex {{index}} supera el límit màxim de testimonis ({{itemTokens}} > {{maxTokens}}). S'està ometent.",
"textWithPrefixExceedsTokenLimit": "El text a l'índex {{index}} amb prefix supera el límit màxim de tokens ({{estimatedTokens}} > {{maxTokens}}). No s'afegirà el prefix.",
"rateLimitRetry": "S'ha assolit el límit de velocitat, es torna a intentar en {{delayMs}}ms (intent {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "No s'ha pogut llegir el cos de l'error",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Erstellung von Einbettungen fehlgeschlagen: Authentifizierung fehlgeschlagen. Bitte überprüfe deinen API-Schlüssel.",
"failedWithStatus": "Erstellung von Einbettungen nach {{attempts}} Versuchen fehlgeschlagen: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "Erstellung von Einbettungen nach {{attempts}} Versuchen fehlgeschlagen: {{errorMessage}}",
"insufficientQuota": "Erstellung von Einbettungen fehlgeschlagen: Unzureichendes Kontingent. Bitte überprüfen Sie Ihr OpenAI-Kontoguthaben und fügen Sie Credits hinzu, um fortzufahren.",
"failedMaxAttempts": "Erstellung von Einbettungen nach {{attempts}} Versuchen fehlgeschlagen",
"textExceedsTokenLimit": "Text bei Index {{index}} überschreitet das maximale Token-Limit ({{itemTokens}} > {{maxTokens}}). Wird übersprungen.",
"textWithPrefixExceedsTokenLimit": "Text bei Index {{index}} mit Präfix überschreitet das maximale Token-Limit ({{estimatedTokens}} > {{maxTokens}}). Präfix wird nicht hinzugefügt.",
"rateLimitRetry": "Ratenlimit erreicht, Wiederholung in {{delayMs}}ms (Versuch {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Fehlerinhalt konnte nicht gelesen werden",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Failed to create embeddings: Authentication failed. Please check your API key.",
"failedWithStatus": "Failed to create embeddings after {{attempts}} attempts: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "Failed to create embeddings after {{attempts}} attempts: {{errorMessage}}",
"insufficientQuota": "Failed to create embeddings: Insufficient quota. Please check your OpenAI account balance and add credits to continue.",
"failedMaxAttempts": "Failed to create embeddings after {{attempts}} attempts",
"textExceedsTokenLimit": "Text at index {{index}} exceeds maximum token limit ({{itemTokens}} > {{maxTokens}}). Skipping.",
"textWithPrefixExceedsTokenLimit": "Text at index {{index}} with prefix exceeds maximum token limit ({{estimatedTokens}} > {{maxTokens}}). Not adding prefix.",
"rateLimitRetry": "Rate limit hit, retrying in {{delayMs}}ms (attempt {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Could not read error body",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "No se pudieron crear las incrustaciones: Error de autenticación. Comprueba tu clave de API.",
"failedWithStatus": "No se pudieron crear las incrustaciones después de {{attempts}} intentos: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "No se pudieron crear las incrustaciones después de {{attempts}} intentos: {{errorMessage}}",
"insufficientQuota": "No se pudieron crear las incrustaciones: Cuota insuficiente. Por favor, verifique el saldo de su cuenta OpenAI y agregue créditos para continuar.",
"failedMaxAttempts": "No se pudieron crear las incrustaciones después de {{attempts}} intentos",
"textExceedsTokenLimit": "El texto en el índice {{index}} supera el límite máximo de tokens ({{itemTokens}} > {{maxTokens}}). Omitiendo.",
"textWithPrefixExceedsTokenLimit": "El texto en el índice {{index}} con prefijo excede el límite máximo de tokens ({{estimatedTokens}} > {{maxTokens}}). No se agrega el prefijo.",
"rateLimitRetry": "Límite de velocidad alcanzado, reintentando en {{delayMs}}ms (intento {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "No se pudo leer el cuerpo del error",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Échec de la création des embeddings : Échec de l'authentification. Veuillez vérifier votre clé API.",
"failedWithStatus": "Échec de la création des embeddings après {{attempts}} tentatives : HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "Échec de la création des embeddings après {{attempts}} tentatives : {{errorMessage}}",
"insufficientQuota": "Échec de la création des embeddings : Quota insuffisant. Veuillez vérifier le solde de votre compte OpenAI et ajouter des crédits pour continuer.",
"failedMaxAttempts": "Échec de la création des embeddings après {{attempts}} tentatives",
"textExceedsTokenLimit": "Le texte à l'index {{index}} dépasse la limite maximale de tokens ({{itemTokens}} > {{maxTokens}}). Ignoré.",
"textWithPrefixExceedsTokenLimit": "Le texte à l'index {{index}} avec préfixe dépasse la limite maximale de jetons ({{estimatedTokens}} > {{maxTokens}}). Ne pas ajouter de préfixe.",
"rateLimitRetry": "Limite de débit atteinte, nouvelle tentative dans {{delayMs}}ms (tentative {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Impossible de lire le corps de l'erreur",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "एम्बेडिंग बनाने में विफल: प्रमाणीकरण विफल। कृपया अपनी एपीआई कुंजी जांचें।",
"failedWithStatus": "{{attempts}} प्रयासों के बाद एम्बेडिंग बनाने में विफल: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "{{attempts}} प्रयासों के बाद एम्बेडिंग बनाने में विफल: {{errorMessage}}",
"insufficientQuota": "एम्बेडिंग बनाने में विफल: अपर्याप्त कोटा। कृपया अपने OpenAI खाते की शेष राशि जांचें और जारी रखने के लिए क्रेडिट जोड़ें।",
"failedMaxAttempts": "{{attempts}} प्रयासों के बाद एम्बेडिंग बनाने में विफल",
"textExceedsTokenLimit": "अनुक्रमणिका {{index}} पर पाठ अधिकतम टोकन सीमा ({{itemTokens}} > {{maxTokens}}) से अधिक है। छोड़ा जा रहा है।",
"textWithPrefixExceedsTokenLimit": "उपसर्ग के साथ इंडेक्स {{index}} पर टेक्स्ट अधिकतम टोकन सीमा ({{estimatedTokens}} > {{maxTokens}}) से अधिक है। उपसर्ग नहीं जोड़ा जा रहा है।",
"rateLimitRetry": "दर सीमा समाप्त, {{delayMs}}ms में पुन: प्रयास किया जा रहा है (प्रयास {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "त्रुटि सामग्री पढ़ नहीं सका",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Gagal membuat embeddings: Autentikasi gagal. Silakan periksa API key Anda.",
"failedWithStatus": "Gagal membuat embeddings setelah {{attempts}} percobaan: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "Gagal membuat embeddings setelah {{attempts}} percobaan: {{errorMessage}}",
"insufficientQuota": "Gagal membuat embeddings: Kuota tidak mencukupi. Silakan periksa saldo akun OpenAI Anda dan tambahkan kredit untuk melanjutkan.",
"failedMaxAttempts": "Gagal membuat embeddings setelah {{attempts}} percobaan",
"textExceedsTokenLimit": "Teks pada indeks {{index}} melebihi batas maksimum token ({{itemTokens}} > {{maxTokens}}). Dilewati.",
"textWithPrefixExceedsTokenLimit": "Teks di indeks {{index}} dengan awalan melebihi batas token maksimum ({{estimatedTokens}} > {{maxTokens}}). Tidak menambahkan awalan.",
"rateLimitRetry": "Batas rate tercapai, mencoba lagi dalam {{delayMs}}ms (percobaan {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Tidak dapat membaca body error",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Creazione degli embedding non riuscita: Autenticazione fallita. Controlla la tua chiave API.",
"failedWithStatus": "Creazione degli embedding non riuscita dopo {{attempts}} tentativi: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "Creazione degli embedding non riuscita dopo {{attempts}} tentativi: {{errorMessage}}",
"insufficientQuota": "Creazione degli embedding non riuscita: Quota insufficiente. Controlla il saldo del tuo account OpenAI e aggiungi crediti per continuare.",
"failedMaxAttempts": "Creazione degli embedding non riuscita dopo {{attempts}} tentativi",
"textExceedsTokenLimit": "Il testo all'indice {{index}} supera il limite massimo di token ({{itemTokens}} > {{maxTokens}}). Saltato.",
"textWithPrefixExceedsTokenLimit": "Il testo all'indice {{index}} con prefisso supera il limite massimo di token ({{estimatedTokens}} > {{maxTokens}}). Non aggiungo il prefisso.",
"rateLimitRetry": "Limite di velocità raggiunto, nuovo tentativo tra {{delayMs}}ms (tentativo {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Impossibile leggere il corpo dell'errore",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "埋め込みの作成に失敗しました認証に失敗しました。APIキーを確認してください。",
"failedWithStatus": "{{attempts}}回試行しましたが、埋め込みの作成に失敗しましたHTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "{{attempts}}回試行しましたが、埋め込みの作成に失敗しました:{{errorMessage}}",
"insufficientQuota": "埋め込みの作成に失敗しましたクォータが不足しています。OpenAIアカウントの残高を確認し、続行するためにクレジットを追加してください。",
"failedMaxAttempts": "{{attempts}}回試行しましたが、埋め込みの作成に失敗しました",
"textExceedsTokenLimit": "インデックス{{index}}のテキストが最大トークン制限を超えています({{itemTokens}}> {{maxTokens}})。スキップします。",
"textWithPrefixExceedsTokenLimit": "インデックス {{index}} のプレフィックス付きテキストが最大トークン制限 ({{estimatedTokens}} > {{maxTokens}}) を超えています。プレフィिक्सを追加しません。",
"rateLimitRetry": "レート制限に達しました。{{delayMs}}ミリ秒後に再試行します(試行{{attempt}}/{{maxRetries}}",
"ollama": {
"couldNotReadErrorBody": "エラー本文を読み取れませんでした",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "임베딩 생성 실패: 인증에 실패했습니다. API 키를 확인하세요.",
"failedWithStatus": "{{attempts}}번 시도 후 임베딩 생성 실패: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "{{attempts}}번 시도 후 임베딩 생성 실패: {{errorMessage}}",
"insufficientQuota": "임베딩 생성 실패: 할당량이 부족합니다. OpenAI 계정 잔액을 확인하고 계속하려면 크레딧을 추가하세요.",
"failedMaxAttempts": "{{attempts}}번 시도 후 임베딩 생성 실패",
"textExceedsTokenLimit": "인덱스 {{index}}의 텍스트가 최대 토큰 제한({{itemTokens}} > {{maxTokens}})을 초과했습니다. 건너뜁니다.",
"textWithPrefixExceedsTokenLimit": "접두사가 있는 인덱스 {{index}}의 텍스트가 최대 토큰 제한({{estimatedTokens}} > {{maxTokens}})을 초과했습니다. 접두사를 추가하지 않습니다.",
"rateLimitRetry": "속도 제한에 도달했습니다. {{delayMs}}ms 후에 다시 시도합니다(시도 {{attempt}}/{{maxRetries}}).",
"ollama": {
"couldNotReadErrorBody": "오류 본문을 읽을 수 없습니다",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Insluitingen maken mislukt: Authenticatie mislukt. Controleer je API-sleutel.",
"failedWithStatus": "Insluitingen maken mislukt na {{attempts}} pogingen: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "Insluitingen maken mislukt na {{attempts}} pogingen: {{errorMessage}}",
"insufficientQuota": "Kan geen embeddings maken: Onvoldoende quota. Controleer uw OpenAI-accountsaldo en voeg tegoeden toe om door te gaan.",
"failedMaxAttempts": "Insluitingen maken mislukt na {{attempts}} pogingen",
"textExceedsTokenLimit": "Tekst op index {{index}} overschrijdt de maximale tokenlimiet ({{itemTokens}} > {{maxTokens}}). Wordt overgeslagen.",
"textWithPrefixExceedsTokenLimit": "Tekst op index {{index}} met prefix overschrijdt de maximale tokenlimiet ({{estimatedTokens}} > {{maxTokens}}). Prefix wordt niet toegevoegd.",
"rateLimitRetry": "Snelheidslimiet bereikt, opnieuw proberen over {{delayMs}}ms (poging {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Kon foutinhoud niet lezen",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Nie udało się utworzyć osadzeń: Uwierzytelnianie nie powiodło się. Sprawdź swój klucz API.",
"failedWithStatus": "Nie udało się utworzyć osadzeń po {{attempts}} próbach: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "Nie udało się utworzyć osadzeń po {{attempts}} próbach: {{errorMessage}}",
"insufficientQuota": "Nie udało się utworzyć osadzeń: Niewystarczający limit. Sprawdź saldo konta OpenAI i dodaj środki, aby kontynuować.",
"failedMaxAttempts": "Nie udało się utworzyć osadzeń po {{attempts}} próbach",
"textExceedsTokenLimit": "Tekst w indeksie {{index}} przekracza maksymalny limit tokenów ({{itemTokens}} > {{maxTokens}}). Pomijanie.",
"textWithPrefixExceedsTokenLimit": "Tekst w indeksie {{index}} z prefiksem przekracza maksymalny limit tokenów ({{estimatedTokens}} > {{maxTokens}}). Nie dodaję prefiksu.",
"rateLimitRetry": "Osiągnięto limit szybkości, ponawianie za {{delayMs}}ms (próba {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Nie można odczytać treści błędu",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Falha ao criar embeddings: Falha na autenticação. Verifique sua chave de API.",
"failedWithStatus": "Falha ao criar embeddings após {{attempts}} tentativas: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "Falha ao criar embeddings após {{attempts}} tentativas: {{errorMessage}}",
"insufficientQuota": "Falha ao criar embeddings: Quota insuficiente. Verifique o saldo da sua conta OpenAI e adicione créditos para continuar.",
"failedMaxAttempts": "Falha ao criar embeddings após {{attempts}} tentativas",
"textExceedsTokenLimit": "O texto no índice {{index}} excede o limite máximo de tokens ({{itemTokens}} > {{maxTokens}}). Ignorando.",
"textWithPrefixExceedsTokenLimit": "O texto no índice {{index}} com prefixo excede o limite máximo de tokens ({{estimatedTokens}} > {{maxTokens}}). Não adicionando prefixo.",
"rateLimitRetry": "Limite de taxa atingido, tentando novamente em {{delayMs}}ms (tentativa {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Não foi possível ler o corpo do erro",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Не удалось создать вложения: Ошибка аутентификации. Проверьте свой ключ API.",
"failedWithStatus": "Не удалось создать вложения после {{attempts}} попыток: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "Не удалось создать вложения после {{attempts}} попыток: {{errorMessage}}",
"insufficientQuota": "Не удалось создать вложения: Недостаточно квоты. Пожалуйста, проверьте баланс вашей учетной записи OpenAI и добавьте кредиты, чтобы продолжить.",
"failedMaxAttempts": "Не удалось создать вложения после {{attempts}} попыток",
"textExceedsTokenLimit": "Текст в индексе {{index}} превышает максимальный лимит токенов ({{itemTokens}} > {{maxTokens}}). Пропускается.",
"textWithPrefixExceedsTokenLimit": "Текст по индексу {{index}} с префиксом превышает максимальный лимит токенов ({{estimatedTokens}} > {{maxTokens}}). Не добавляю префикс.",
"rateLimitRetry": "Достигнут лимит скорости, повторная попытка через {{delayMs}} мс (попытка {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Не удалось прочитать тело ошибки",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Gömülmeler oluşturulamadı: Kimlik doğrulama başarısız oldu. Lütfen API anahtarınızı kontrol edin.",
"failedWithStatus": "{{attempts}} denemeden sonra gömülmeler oluşturulamadı: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "{{attempts}} denemeden sonra gömülmeler oluşturulamadı: {{errorMessage}}",
"insufficientQuota": "Gömülmeler oluşturulamadı: Yetersiz kota. Lütfen OpenAI hesap bakiyenizi kontrol edin ve devam etmek için kredi ekleyin.",
"failedMaxAttempts": "{{attempts}} denemeden sonra gömülmeler oluşturulamadı",
"textExceedsTokenLimit": "{{index}} dizinindeki metin maksimum jeton sınırınııyor ({{itemTokens}} > {{maxTokens}}). Atlanıyor.",
"textWithPrefixExceedsTokenLimit": "{{index}} dizinindeki önekli metin maksimum jeton sınırınııyor ({{estimatedTokens}} > {{maxTokens}}). Önek eklenmiyor.",
"rateLimitRetry": "Hız sınırına ulaşıldı, {{delayMs}}ms içinde yeniden deneniyor (deneme {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Hata gövdesi okunamadı",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "Không thể tạo nhúng: Xác thực không thành công. Vui lòng kiểm tra khóa API của bạn.",
"failedWithStatus": "Không thể tạo nhúng sau {{attempts}} lần thử: HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "Không thể tạo nhúng sau {{attempts}} lần thử: {{errorMessage}}",
"insufficientQuota": "Không tạo được embedding: Không đủ hạn ngạch. Vui lòng kiểm tra số dư tài khoản OpenAI của bạn và nạp thêm tín dụng để tiếp tục.",
"failedMaxAttempts": "Không thể tạo nhúng sau {{attempts}} lần thử",
"textExceedsTokenLimit": "Văn bản tại chỉ mục {{index}} vượt quá giới hạn mã thông báo tối đa ({{itemTokens}} > {{maxTokens}}). Bỏ qua.",
"textWithPrefixExceedsTokenLimit": "Văn bản tại chỉ mục {{index}} có tiền tố vượt quá giới hạn mã thông báo tối đa ({{estimatedTokens}} > {{maxTokens}}). Không thêm tiền tố.",
"rateLimitRetry": "Đã đạt đến giới hạn tốc độ, thử lại sau {{delayMs}}ms (lần thử {{attempt}}/{{maxRetries}})",
"ollama": {
"couldNotReadErrorBody": "Không thể đọc nội dung lỗi",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "创建嵌入失败:身份验证失败。请检查您的 API 密钥。",
"failedWithStatus": "尝试 {{attempts}} 次后创建嵌入失败HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "尝试 {{attempts}} 次后创建嵌入失败:{{errorMessage}}",
"insufficientQuota": "创建嵌入失败:配额不足。请检查您的 OpenAI 帐户余额並添加积分以继续。",
"failedMaxAttempts": "尝试 {{attempts}} 次后创建嵌入失败",
"textExceedsTokenLimit": "索引 {{index}} 处的文本超过最大令牌限制 ({{itemTokens}} > {{maxTokens}})。正在跳过。",
"textWithPrefixExceedsTokenLimit": "索引 {{index}} 处带前缀的文本超过了最大令牌限制 ({{estimatedTokens}} > {{maxTokens}})。不添加前缀。",
"rateLimitRetry": "已达到速率限制,将在 {{delayMs}} 毫秒后重试(尝试次数 {{attempt}}/{{maxRetries}}",
"ollama": {
"couldNotReadErrorBody": "无法读取错误内容",

View file

@ -3,8 +3,10 @@
"authenticationFailed": "建立內嵌失敗:驗證失敗。請檢查您的 API 金鑰。",
"failedWithStatus": "嘗試 {{attempts}} 次後建立內嵌失敗HTTP {{statusCode}} - {{errorMessage}}",
"failedWithError": "嘗試 {{attempts}} 次後建立內嵌失敗:{{errorMessage}}",
"insufficientQuota": "建立嵌入失敗:額度不足。請檢查您的 OpenAI 帳戶餘額並新增點數以繼續。",
"failedMaxAttempts": "嘗試 {{attempts}} 次後建立內嵌失敗",
"textExceedsTokenLimit": "索引 {{index}} 處的文字超過最大權杖限制 ({{itemTokens}} > {{maxTokens}})。正在略過。",
"textWithPrefixExceedsTokenLimit": "索引 {{index}} 處帶前綴的文本超過了最大令牌限制 ({{estimatedTokens}} > {{maxTokens}})。不添加前綴。",
"rateLimitRetry": "已達到速率限制,將在 {{delayMs}} 毫秒後重試(嘗試次數 {{attempt}}/{{maxRetries}}",
"ollama": {
"couldNotReadErrorBody": "無法讀取錯誤內容",

View file

@ -21,6 +21,8 @@ vitest.mock("../../../../i18n", () => ({
"embeddings:textExceedsTokenLimit": `Text at index ${params?.index} exceeds maximum token limit (${params?.itemTokens} > ${params?.maxTokens}). Skipping.`,
"embeddings:rateLimitRetry": `Rate limit hit, retrying in ${params?.delayMs}ms (attempt ${params?.attempt}/${params?.maxRetries})`,
"embeddings:unknownError": "Unknown error",
"embeddings:insufficientQuota":
"Failed to create embeddings: Insufficient quota. Please check your OpenAI account balance and add credits to continue.",
}
return translations[key] || key
},
@ -405,6 +407,48 @@ describe("OpenAICompatibleEmbedder", () => {
expect(console.warn).not.toHaveBeenCalledWith(expect.stringContaining("Rate limit hit"))
})
it("should not retry on insufficient quota errors", async () => {
const testTexts = ["Hello world"]
const quotaError = new Error(
"You exceeded your current quota, please check your plan and billing details.",
)
;(quotaError as any).status = 429
mockEmbeddingsCreate.mockRejectedValue(quotaError)
await expect(embedder.createEmbeddings(testTexts)).rejects.toThrow(
"Failed to create embeddings: Insufficient quota. Please check your OpenAI account balance and add credits to continue.",
)
expect(mockEmbeddingsCreate).toHaveBeenCalledTimes(1)
expect(console.warn).not.toHaveBeenCalledWith(expect.stringContaining("Rate limit hit"))
})
it("should retry on regular rate limit errors", async () => {
const testTexts = ["Hello world"]
const rateLimitError = new Error("Rate limit exceeded")
;(rateLimitError as any).status = 429
// Create base64 encoded embedding for successful response
const testEmbedding = new Float32Array([0.25, 0.5, 0.75])
const base64String = Buffer.from(testEmbedding.buffer).toString("base64")
mockEmbeddingsCreate.mockRejectedValueOnce(rateLimitError).mockResolvedValueOnce({
data: [{ embedding: base64String }],
usage: { prompt_tokens: 10, total_tokens: 15 },
})
const resultPromise = embedder.createEmbeddings(testTexts)
await vitest.advanceTimersByTimeAsync(INITIAL_RETRY_DELAY_MS)
const result = await resultPromise
expect(mockEmbeddingsCreate).toHaveBeenCalledTimes(2)
expect(result).toEqual({
embeddings: [[0.25, 0.5, 0.75]],
usage: { promptTokens: 10, totalTokens: 15 },
})
})
it("should throw error immediately on non-retryable errors", async () => {
const testTexts = ["Hello world"]
const serverError = new Error("Internal server error")

View file

@ -18,6 +18,8 @@ vitest.mock("../../../../i18n", () => ({
"embeddings:failedMaxAttempts": `Failed to create embeddings after ${params?.attempts} attempts`,
"embeddings:textExceedsTokenLimit": `Text at index ${params?.index} exceeds maximum token limit (${params?.itemTokens} > ${params?.maxTokens}). Skipping.`,
"embeddings:rateLimitRetry": `Rate limit hit, retrying in ${params?.delayMs}ms (attempt ${params?.attempt}/${params?.maxRetries})`,
"embeddings:insufficientQuota":
"Failed to create embeddings: Insufficient quota. Please check your OpenAI account balance and add credits to continue.",
}
return translations[key] || key
},
@ -307,6 +309,44 @@ describe("OpenAiEmbedder", () => {
expect(console.warn).not.toHaveBeenCalledWith(expect.stringContaining("Rate limit hit"))
})
it("should not retry on insufficient quota errors", async () => {
const testTexts = ["Hello world"]
const quotaError = new Error(
"You exceeded your current quota, please check your plan and billing details.",
)
;(quotaError as any).status = 429
mockEmbeddingsCreate.mockRejectedValue(quotaError)
await expect(embedder.createEmbeddings(testTexts)).rejects.toThrow(
"Failed to create embeddings: Insufficient quota. Please check your OpenAI account balance and add credits to continue.",
)
expect(mockEmbeddingsCreate).toHaveBeenCalledTimes(1)
expect(console.warn).not.toHaveBeenCalledWith(expect.stringContaining("Rate limit hit"))
})
it("should retry on regular rate limit errors", async () => {
const testTexts = ["Hello world"]
const rateLimitError = new Error("Rate limit exceeded")
;(rateLimitError as any).status = 429
mockEmbeddingsCreate.mockRejectedValueOnce(rateLimitError).mockResolvedValueOnce({
data: [{ embedding: [0.1, 0.2, 0.3] }],
usage: { prompt_tokens: 10, total_tokens: 15 },
})
const resultPromise = embedder.createEmbeddings(testTexts)
await vitest.advanceTimersByTimeAsync(INITIAL_RETRY_DELAY_MS)
const result = await resultPromise
expect(mockEmbeddingsCreate).toHaveBeenCalledTimes(2)
expect(result).toEqual({
embeddings: [[0.1, 0.2, 0.3]],
usage: { promptTokens: 10, totalTokens: 15 },
})
})
it("should throw error immediately on non-retryable errors", async () => {
const testTexts = ["Hello world"]
const serverError = new Error("Internal server error")

View file

@ -276,7 +276,10 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
const isRateLimitError = httpError?.status === 429
const hasMoreAttempts = attempts < MAX_RETRIES - 1
if (isRateLimitError && hasMoreAttempts) {
// Add quota detection
const isQuotaError = this.isInsufficientQuotaError(error)
if (isRateLimitError && !isQuotaError && hasMoreAttempts) {
const delayMs = INITIAL_DELAY_MS * Math.pow(2, attempts)
console.warn(
t("embeddings:rateLimitRetry", {
@ -287,6 +290,9 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
)
await new Promise((resolve) => setTimeout(resolve, delayMs))
continue
} else if (isQuotaError) {
// Throw specific quota error immediately
throw new Error(t("embeddings:insufficientQuota"))
}
// Log the error for debugging
@ -331,4 +337,31 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
name: "openai-compatible",
}
}
/**
* Detects if an error is due to insufficient quota/credits
* @param error The error object to check
* @returns True if the error indicates insufficient quota
*/
private isInsufficientQuotaError(error: any): boolean {
if (error?.status !== 429) return false
const errorMessage =
error?.message?.toLowerCase() ||
error?.response?.data?.error?.message?.toLowerCase() ||
error?.error?.message?.toLowerCase() ||
""
const quotaKeywords = [
"insufficient_quota",
"insufficient quota",
"quota exceeded",
"insufficient funds",
"billing",
"payment required",
"credits",
]
return quotaKeywords.some((keyword) => errorMessage.includes(keyword))
}
}

View file

@ -141,7 +141,10 @@ export class OpenAiEmbedder extends OpenAiNativeHandler implements IEmbedder {
const isRateLimitError = error?.status === 429
const hasMoreAttempts = attempts < MAX_RETRIES - 1
if (isRateLimitError && hasMoreAttempts) {
// Add quota detection
const isQuotaError = this.isInsufficientQuotaError(error)
if (isRateLimitError && !isQuotaError && hasMoreAttempts) {
const delayMs = INITIAL_DELAY_MS * Math.pow(2, attempts)
console.warn(
t("embeddings:rateLimitRetry", {
@ -152,6 +155,9 @@ export class OpenAiEmbedder extends OpenAiNativeHandler implements IEmbedder {
)
await new Promise((resolve) => setTimeout(resolve, delayMs))
continue
} else if (isQuotaError) {
// Throw specific quota error immediately
throw new Error(t("embeddings:insufficientQuota"))
}
// Log the error for debugging
@ -193,4 +199,31 @@ export class OpenAiEmbedder extends OpenAiNativeHandler implements IEmbedder {
name: "openai",
}
}
/**
* Detects if an error is due to insufficient quota/credits
* @param error The error object to check
* @returns True if the error indicates insufficient quota
*/
private isInsufficientQuotaError(error: any): boolean {
if (error?.status !== 429) return false
const errorMessage =
error?.message?.toLowerCase() ||
error?.response?.data?.error?.message?.toLowerCase() ||
error?.error?.message?.toLowerCase() ||
""
const quotaKeywords = [
"insufficient_quota",
"insufficient quota",
"quota exceeded",
"insufficient funds",
"billing",
"payment required",
"credits",
]
return quotaKeywords.some((keyword) => errorMessage.includes(keyword))
}
}