{"record":{"id":"edca318b3b9120b3","repo":"stablyai/orca","slug":"translation-request-failed-with-status-response","errorCode":null,"errorMessage":"Translation request failed with status ${response.status}","messagePattern":"Translation request failed with status (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config/scripts/bootstrap-locale-catalog.mjs","lineNumber":78,"sourceCode":"\nfunction shouldSkipTranslation(text) {\n  return shouldPreserveEnglishValue(text)\n}\n\nasync function translateText(text, targetLanguage) {\n  const url = new URL('https://translate.googleapis.com/translate_a/single')\n  url.searchParams.set('client', 'gtx')\n  url.searchParams.set('sl', 'en')\n  url.searchParams.set('tl', targetLanguage)\n  url.searchParams.set('dt', 't')\n  url.searchParams.set('q', text)\n\n  let lastError\n  for (let attempt = 0; attempt < 5; attempt += 1) {\n    try {\n      const response = await fetch(url)\n      if (!response.ok) {\n        throw new Error(`Translation request failed with status ${response.status}`)\n      }\n      const payload = await response.json()\n      return payload[0].map((part) => part[0]).join('')\n    } catch (error) {\n      lastError = error\n      await new Promise((resolve) => setTimeout(resolve, 500 * (attempt + 1)))\n    }\n  }\n  throw lastError\n}\n\nasync function mapWithConcurrency(items, concurrency, mapper) {\n  const results = Array.from({ length: items.length })\n  let nextIndex = 0\n\n  async function worker() {\n    while (nextIndex < items.length) {\n      const currentIndex = nextIndex","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/bootstrap-locale-catalog.mjs#L60-L96","documentation":"Thrown by translateText in bootstrap-locale-catalog.mjs when the Google Translate gtx endpoint returns a non-OK HTTP status. The function retries up to 5 times with linear backoff (500, 1000, 1500, 2000, 2500 ms) before rethrowing the last error; this error specifically signals an HTTP-level failure (4xx/5xx) rather than a network exception.","triggerScenarios":"Rate limiting (HTTP 429) from the free gtx endpoint; IP-based blocking (403/429) after many requests in a short window; the endpoint returning 502/503 during a Google outage; sending a payload that exceeds the query-string length limit (414).","commonSituations":"Re-running the locale bootstrap on a fresh machine many times in a day; large catalog with many unique strings hitting the per-IP quota; corporate egress IP shared across CI runners triggering the same rate limit; a single very long string blowing past the URL length cap.","solutions":["Wait and retry — the gtx quota resets over time; the script's built-in 5 retries with linear backoff handle transient 429s but a sustained block needs a longer pause.","Reduce the workload: the script caches translated values in .<locale>-catalog-cache.json, so keep that cache and only translate new strings.","Lower concurrency from 2 to 1 (mapWithConcurrency third arg) and/or increase the 200ms throttle between requests to stay under the rate limit.","For very long strings, split them before translation to avoid HTTP 414 URI-Too-Long.","If the block is permanent for your IP, run from a different network or pre-translate offline and populate the cache file manually."],"exampleFix":"// before — full catalog, concurrency 2, no throttle headroom\n// await mapWithConcurrency(toTranslate, 2, async (value) => { ... await new Promise(r => setTimeout(r, 200)) })\n\n// after — concurrency 1, longer throttle, reuse cache\n// await mapWithConcurrency(toTranslate, 1, async (value) => { ... await new Promise(r => setTimeout(r, 800)) })","handlingStrategy":"retry","validationCode":"async function translateWithBudget(text, targetLanguage, maxAttempts = 5) {\n  // Pre-split very long strings to avoid HTTP 414.\n  const MAX_QUERY = 1500\n  if (text.length > MAX_QUERY) {\n    throw new Error(`Refusing to translate: text length ${text.length} exceeds ${MAX_QUERY} (would 414).`)\n  }\n  return translateText(text, targetLanguage, maxAttempts)\n}","typeGuard":null,"tryCatchPattern":"try {\n  await translateText(value, lang)\n} catch (error) {\n  if (/Translation request failed with status 429|403|503/.test(error.message)) {\n    console.error(`Translation blocked (${error.message}). Keeping cached value; rerun later.`)\n    return cache.get(value) ?? value\n  }\n  throw error\n}","preventionTips":["Always keep the .<locale>-catalog-cache.json file between runs so only new strings are translated.","Run bootstrap-locale-catalog at low concurrency and with the 200ms throttle — the free gtx endpoint rate-limits aggressively.","Split very long source strings before translating to avoid HTTP 414 URI-Too-Long.","Run from a stable egress IP; shared CI runners often inherit a rate-limited quota."],"tags":["i18n","network","translation","rate-limit","retry"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}