{"record":{"id":"11589575c1724cba","repo":"chroma-core/chroma","slug":"error-calling-cloudflare-workers-ai-api-error-m","errorCode":null,"errorMessage":"Error calling Cloudflare Workers AI API: ${error.message}","messagePattern":"Error calling Cloudflare Workers AI API: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clients/js/packages/chromadb-core/src/embeddings/CloudflareWorkersAIEmbeddingFunction.ts","lineNumber":85,"sourceCode":"        text: texts,\n      };\n\n      const response = await fetch(this.api_url, {\n        method: \"POST\",\n        headers: this.headers,\n        body: JSON.stringify(payload),\n      });\n\n      const resp = await response.json();\n\n      if (!resp.result || !resp.result.data) {\n        throw new Error(resp.detail || \"Unknown error\");\n      }\n\n      return resp.result.data;\n    } catch (error) {\n      if (error instanceof Error) {\n        throw new Error(\n          `Error calling Cloudflare Workers AI API: ${error.message}`,\n        );\n      } else {\n        throw new Error(`Error calling Cloudflare Workers AI API: ${error}`);\n      }\n    }\n  }\n\n  buildFromConfig(config: StoredConfig): CloudflareWorkersAIEmbeddingFunction {\n    return new CloudflareWorkersAIEmbeddingFunction({\n      model_name: config.model_name,\n      account_id: config.account_id,\n      api_key_env_var: config.api_key_env_var,\n      gateway_id: config.gateway_id ?? undefined,\n    });\n  }\n\n  getConfig(): StoredConfig {","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/clients/js/packages/chromadb-core/src/embeddings/CloudflareWorkersAIEmbeddingFunction.ts#L67-L103","documentation":"The catch-all wrapper in CloudflareWorkersAIEmbeddingFunction.generate: any Error thrown in the try block - fetch network failures (DNS, ECONNREFUSED, timeouts), response.json() parse failures on non-JSON bodies (HTML error pages), and the inner 'resp.detail || Unknown error' throw - is re-thrown as 'Error calling Cloudflare Workers AI API: <original.message>'. This is the error you actually see in stack traces from this embedding function; the original cause is preserved only inside the message text, not as error.cause.","triggerScenarios":"await ef.generate(texts) while offline/DNS-blocked; corporate proxy rejecting api.cloudflare.com; 502/52x returning HTML so response.json() throws SyntaxError; the inner Unknown error throw being re-wrapped (double-wrapping).","commonSituations":"CI without external network egress; firewall/proxy whitelisting gaps; transient Cloudflare gateway incidents; response body unexpectedly HTML (auth wall, captive portal).","solutions":["Read the suffix after 'Error calling Cloudflare Workers AI API:' - 'fetch failed' means network, a JSON SyntaxError means a non-JSON error page, other text is the API's detail.","For transient network issues, retry with backoff around generate().","Verify egress to api.cloudflare.com (curl) and proxy env vars (HTTPS_PROXY) in the failing environment.","If the suffix is 'Unknown error' or a Cloudflare detail, debug per the API-error case (model/account/key)."],"exampleFix":"// before\nconst embs = await ef.generate(texts); // opaque wrapped failure\n\n// after\nasync function embedWithRetry(texts: string[], attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await ef.generate(texts);\n    } catch (e) {\n      const msg = (e as Error).message;\n      if (i === attempts - 1 || !msg.includes('fetch failed')) throw e;\n      await new Promise((r) => setTimeout(r, 2 ** i * 500));\n    }\n  }\n  throw new Error('unreachable');\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"async function generateWithRetry(texts: string[], tries = 3) {\n  let lastErr: unknown;\n  for (let i = 0; i < tries; i++) {\n    try {\n      return await ef.generate(texts);\n    } catch (e) {\n      lastErr = e;\n      const msg = (e as Error).message ?? '';\n      const transient =\n        msg.includes('fetch failed') || msg.includes('ETIMEDOUT') || msg.includes('ECONNRESET');\n      if (!transient || i === tries - 1) break;\n      await new Promise((r) => setTimeout(r, 2 ** i * 500));\n    }\n  }\n  throw lastErr;\n}","preventionTips":["Retry only transient-looking failures (fetch failed/timeouts), never auth or model errors.","Use exponential backoff with a cap to respect Cloudflare rate limits.","Verify egress/proxy settings in locked-down environments before deploying.","Treat HTML error pages (json parse errors in the message) as infrastructure misconfiguration, not retryable."],"tags":["embeddings","cloudflare","network","error-wrapping","runtime"],"backgroundTag":"api-request-failed","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}