CherryHQ/cherry-studio · error · Error

Rerank response results must be objects

Error message

Rerank response results must be objects

What it means

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.

Source

Thrown at packages/ai-sdk-provider/src/openai-compatible-reranking-model.ts:93

    return {
      ranking: value,
      response: {
        body: rawValue
      }
    }
  }
}

function parseRerankResponse(body: unknown, documentCount: number): RerankRanking {
  const results = (body as OpenAICompatibleRerankResponse).results
  if (!Array.isArray(results)) {
    throw new Error('Rerank response must contain a results array')
  }

  return results.map((result) => {
    if (typeof result !== 'object' || result === null) {
      throw new Error('Rerank response results must be objects')
    }

    if (typeof result.index !== 'number' || typeof result.relevance_score !== 'number') {
      throw new Error('Rerank response results must contain numeric index and relevance_score')
    }

    if (!Number.isInteger(result.index) || result.index < 0 || result.index >= documentCount) {
      throw new Error('Rerank response results must reference a valid document index')
    }

    return { index: result.index, relevanceScore: result.relevance_score }
  })
}

export function createOpenAICompatibleRerankingModel(
  modelId: string,
  settings: OpenAICompatibleRerankingModelSettings
): RerankingModelV3 {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Implement `poll(taskId, options)` on the transport object so the returned task id can be resolved to URLs.
  2. If the model is actually synchronous, do NOT return a taskId from submit() — return imageUrls directly so pollUntilDone is never entered.
  3. Register the transport through imageTransportRegistry only after both submit and poll (for async) are present.
  4. Add a unit test that asserts: if submit can return taskId, then transport.poll is a function.

Example fix

// before: async vendor, but poll missing
export const myTransport: ImageGenerationTransport = {
  async submit(input) {
    const body = await post(url, input)
    return { taskId: body.task_id }   // poll() never defined
  }
}
// after: implement poll to resolve the task
export const myTransport: ImageGenerationTransport = {
  async submit(input) {
    const body = await post(url, input)
    return { taskId: body.task_id }
  },
  async poll(taskId, { signal }) {
    const body = await get(`${url}/${taskId}`, { signal })
    if (body.status !== 'SUCCEEDED') throw new Error(`task ${body.status}`)
    return body.output.map(o => o.url)
  }
}
Defensive patterns

Strategy: type-guard

Type guard

// Assert at adapter build time that a transport returning taskId also polls.
function assertPollIfAsync(t: ImageGenerationTransport): void {
  // submit can return taskId at runtime; enforce poll presence statically by contract.
  if (typeof t.poll !== 'function') {
    // mark this transport as sync-only and ensure submit never yields a taskId
    console.warn('Transport lacks poll(); submit must return imageUrls only')
  }
}
// Stronger: a branded type for async transports
interface AsyncImageTransport extends ImageGenerationTransport { poll: NonNullable<ImageGenerationTransport['poll']> }

Try / catch

try {
  await generateImageViaJob(payload)
} catch (e) {
  if (e instanceof Error && /does not implement polling/.test(e.message)) {
    // this is an adapter bug, not a user error — report it, do not retry
    reportBug(`Transport for ${modelId} returned a taskId without poll()`)
  } else throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/8d793c3f84a46458. Report an issue: GitHub.