{"record":{"id":"ec2e3528efe0da40","repo":"CherryHQ/cherry-studio","slug":"rerank-response-results-must-contain-numeric-index","errorCode":null,"errorMessage":"Rerank response results must contain numeric index and relevance_score","messagePattern":"Rerank response results must contain numeric index and relevance_score","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ai-sdk-provider/src/openai-compatible-reranking-model.ts","lineNumber":97,"sourceCode":"        body: rawValue\n      }\n    }\n  }\n}\n\nfunction parseRerankResponse(body: unknown, documentCount: number): RerankRanking {\n  const results = (body as OpenAICompatibleRerankResponse).results\n  if (!Array.isArray(results)) {\n    throw new Error('Rerank response must contain a results array')\n  }\n\n  return results.map((result) => {\n    if (typeof result !== 'object' || result === null) {\n      throw new Error('Rerank response results must be objects')\n    }\n\n    if (typeof result.index !== 'number' || typeof result.relevance_score !== 'number') {\n      throw new Error('Rerank response results must contain numeric index and relevance_score')\n    }\n\n    if (!Number.isInteger(result.index) || result.index < 0 || result.index >= documentCount) {\n      throw new Error('Rerank response results must reference a valid document index')\n    }\n\n    return { index: result.index, relevanceScore: result.relevance_score }\n  })\n}\n\nexport function createOpenAICompatibleRerankingModel(\n  modelId: string,\n  settings: OpenAICompatibleRerankingModelSettings\n): RerankingModelV3 {\n  const baseURL = withoutTrailingSlash(settings.baseURL)\n  if (!baseURL) {\n    throw new Error('OpenAI-compatible reranking model requires baseURL')\n  }","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ai-sdk-provider/src/openai-compatible-reranking-model.ts#L79-L115","documentation":"Thrown by downloadAndPersistImageUrls() when the remote generation succeeded and returned one or more URLs, but every single download+persistence attempt failed (files.length === 0). Each URL is fetched, base64-encoded, and turned into an internal file_entry via fileManager.createInternalEntry; if none survive, the job fails to avoid reporting a paid generation as an empty success. A partial failure (some succeeded) does NOT throw — it logs a warning and returns what it has.","triggerScenarios":"All returned image URLs are expired/revoked by the time download runs (short-lived signed URLs); the host cannot reach the vendor's CDN/edge due to firewall or DNS; disk full or the file_entry store is unwritable so every createInternalEntry rejects; a proxy intercepts and blocks the image hosts; the media_type/data parsing throws for every entry.","commonSituations":"Network egress restrictions in a containerized/sandboxed main process blocking the image CDN; a long poll delay causing signed URLs to expire before download; disk pressure on the userData volume; corporate proxy certificate rejection on the image host; vendor CDN regional outage.","solutions":["Check host network egress to the image URLs' hostnames and TLS/proxy settings in the main process.","Verify free disk space and write permissions on the userData/file_entry storage location.","If URLs expire, download sooner (reduce work between poll completion and download) or request longer-lived URLs via providerOptions if the vendor supports it.","Retry the job once after a transient CDN/network blip — a single retry is cheap since the failure was post-generation (note: job retry is capped at 1 attempt by defaultRetryPolicy, so re-enqueue from the UI).","Inspect the warn logs for individual download errors to see whether it was a network failure, a parse failure, or a persistence failure."],"exampleFix":"// before: downloads happen with no per-URL diagnostics on total failure\nfor (const url of urls) {\n  const downloaded = await downloadImageAsBase64(url, signal)\n  files.push(await fileManager.createInternalEntry({ source: 'base64', data: toDataUrl(downloaded), cleanupPolicy }))\n}\n// after: capture per-URL failure reasons so the thrown error is diagnosable\nconst failures: string[] = []\nfor (const url of urls) {\n  try {\n    const downloaded = await downloadImageAsBase64(url, signal)\n    files.push(await fileManager.createInternalEntry({ source: 'base64', data: toDataUrl(downloaded), cleanupPolicy }))\n  } catch (e) { failures.push(`${url}: ${e instanceof Error ? e.message : String(e)}`) }\n}\nif (files.length === 0) throw new Error(`Image generation produced ${urls.length} URL(s) but all downloads failed [${failures.join('; ')}]`)","handlingStrategy":"retry","validationCode":"// Best-effort preflight that the host can reach the vendor CDN host\n// before paying for generation (cheap HEAD/GET to the image host root).\nasync function canReachCdn(host: string): Promise<boolean> {\n  try { await fetch(`https://${host}/`, { method: 'HEAD' }); return true }\n  catch { return false }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await generateImageViaJob(payload)\n} catch (e) {\n  if (e instanceof Error && /all downloads failed/.test(e.message)) {\n    // post-generation failure: a single re-enqueue is cheap and often recovers a transient CDN blip\n    await retryGenerateImageViaJob(payload)\n  } else throw e\n}","preventionTips":["Whitelist the vendor image-CDN hostnames in the main process network egress rules.","Monitor free disk space on the userData volume where file_entry blobs are written.","Download URLs immediately after poll completes to avoid signed-URL expiry windows."],"tags":["image-generation","download","network","file-persistence","cdn"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}