{"record":{"id":"8d793c3f84a46458","repo":"CherryHQ/cherry-studio","slug":"rerank-response-results-must-be-objects","errorCode":null,"errorMessage":"Rerank response results must be objects","messagePattern":"Rerank response results must be objects","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ai-sdk-provider/src/openai-compatible-reranking-model.ts","lineNumber":93,"sourceCode":"\n    return {\n      ranking: value,\n      response: {\n        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 {","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ai-sdk-provider/src/openai-compatible-reranking-model.ts#L75-L111","documentation":"Thrown inside pollUntilDone() when a transport returned a `taskId` from submit() (signaling async/poll mode) but the same transport does not implement the optional `poll()` method. On the ImageGenerationTransport interface `submit` may return a `taskId` and `poll` is optional — so returning a task id without a poll implementation is an inconsistent adapter contract, not a runtime/config problem. This is a bug in the provider/transport registration, surfaced at the first async submission for that model.","triggerScenarios":"A new custom-provider image transport adapter returns `{ taskId }` from submit() (because the vendor is asynchronous) but the developer forgot to implement the `poll` method on the same object; or an existing sync transport was edited to forward a task id from the raw vendor body without adding polling.","commonSituations":"Adding a new image-generation provider (mirroring ppio/dashscope) and copying the submit shape but omitting poll; refactoring a transport from sync to async and missing the poll method; a transport object built from a partial/factory that conditionally omits poll.","solutions":["Implement `poll(taskId, options)` on the transport object so the returned task id can be resolved to URLs.","If the model is actually synchronous, do NOT return a taskId from submit() — return imageUrls directly so pollUntilDone is never entered.","Register the transport through imageTransportRegistry only after both submit and poll (for async) are present.","Add a unit test that asserts: if submit can return taskId, then transport.poll is a function."],"exampleFix":"// before: async vendor, but poll missing\nexport const myTransport: ImageGenerationTransport = {\n  async submit(input) {\n    const body = await post(url, input)\n    return { taskId: body.task_id }   // poll() never defined\n  }\n}\n// after: implement poll to resolve the task\nexport const myTransport: ImageGenerationTransport = {\n  async submit(input) {\n    const body = await post(url, input)\n    return { taskId: body.task_id }\n  },\n  async poll(taskId, { signal }) {\n    const body = await get(`${url}/${taskId}`, { signal })\n    if (body.status !== 'SUCCEEDED') throw new Error(`task ${body.status}`)\n    return body.output.map(o => o.url)\n  }\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Assert at adapter build time that a transport returning taskId also polls.\nfunction assertPollIfAsync(t: ImageGenerationTransport): void {\n  // submit can return taskId at runtime; enforce poll presence statically by contract.\n  if (typeof t.poll !== 'function') {\n    // mark this transport as sync-only and ensure submit never yields a taskId\n    console.warn('Transport lacks poll(); submit must return imageUrls only')\n  }\n}\n// Stronger: a branded type for async transports\ninterface AsyncImageTransport extends ImageGenerationTransport { poll: NonNullable<ImageGenerationTransport['poll']> }","tryCatchPattern":"try {\n  await generateImageViaJob(payload)\n} catch (e) {\n  if (e instanceof Error && /does not implement polling/.test(e.message)) {\n    // this is an adapter bug, not a user error — report it, do not retry\n    reportBug(`Transport for ${modelId} returned a taskId without poll()`)\n  } else throw e\n}","preventionTips":["When adding a transport, write a unit test: 'if submit returns taskId, transport.poll is a function'.","Use a typed factory (e.g. createAsyncTransport) that requires both submit and poll, vs createSyncTransport that forbids taskId.","Review the imageTransportRegistry registration to confirm poll is bound for every async provider."],"tags":["image-generation","transport","adapter-contract","async-polling"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}