CherryHQ/cherry-studio · error · Error
OpenAI-compatible reranking model only supports text documen
Error message
OpenAI-compatible reranking model only supports text documents
What it means
Thrown by the custom-provider async image-generation job when a transport's submit() returns a response carrying neither `imageUrls` nor a `taskId` (both fields are optional on the submit return type). The handler treats this as a malformed vendor response and fails the job hard, rather than silently completing with zero generated files — which would be a paid no-op the user never sees. It is distinct from error 401 (a successful but empty URL list) and from 402 (a poll-less transport returning a task id).
Source
Thrown at packages/ai-sdk-provider/src/openai-compatible-reranking-model.ts:52
type OpenAICompatibleRerankResponse = {
results?: OpenAICompatibleRerankResponseItem[]
}
export class OpenAICompatibleRerankingModel implements RerankingModelV3 {
readonly specificationVersion = 'v3'
constructor(
readonly modelId: string,
private readonly config: OpenAICompatibleRerankingModelConfig
) {}
get provider(): string {
return this.config.provider
}
async doRerank({ documents, headers, query, topN, abortSignal }: DoRerankOptions): Promise<DoRerankResult> {
if (documents.type !== 'text') {
throw new Error('OpenAI-compatible reranking model only supports text documents')
}
const { value, rawValue } = await postJsonToApi({
url: this.config.url({ path: '/rerank', modelId: this.modelId }),
headers: combineHeaders(this.config.headers(), headers),
body: {
model: this.modelId,
query,
documents: documents.values,
top_n: topN
},
failedResponseHandler: createStatusCodeErrorResponseHandler(),
successfulResponseHandler: async ({ response }) => {
const rawValue = await response.json()
return {
value: parseRerankResponse(rawValue, documents.values.length),
rawValue
}View on GitHub (pinned to 726446b54c)
Solutions
- Capture the raw submit response body (add a temporary log in the transport's submit()) and compare its keys against what the adapter expects to populate imageUrls/taskId from.
- Verify the provider's apiKey and quota in the vendor console — an auth/quota rejection masquerading as a 200 body is the most common cause.
- Confirm sdkConfig.modelId matches a model the vendor actually serves for image generation on that endpoint.
- If the vendor changed its response schema, update the transport adapter in the matching provider package (ppio/dashscope/modelscope/dmxapi) to map the new fields onto imageUrls or taskId.
- Check the vendor's API changelog for the model family in use.
Example fix
// before: transport parses only the happy-path fields
submit(input) {
const body = await fetchJson(url, req)
return { imageUrls: body.data?.map(d => d.url) }
}
// after: surface non-2xx / error envelopes so submit() never returns a bare {}
submit(input) {
const body = await fetchJson(url, req)
if (body.code || body.error) {
throw new Error(`vendor rejected submit: ${body.message ?? JSON.stringify(body)}`)
}
return { imageUrls: body.data?.map(d => d.url), taskId: body.task_id }
} Defensive patterns
Strategy: validation
Validate before calling
// Before invoking the job, assert the transport is one the registry knows
// and that submit() cannot return a bare object. Validate at adapter
// registration time so a malformed submit fails loudly in tests, not in prod.
function assertTransportContract(t: ImageGenerationTransport) {
if (typeof t.submit !== 'function') throw new Error('transport.submit missing')
// optional poll, but if absent the adapter MUST guarantee sync imageUrls
}
// In the job handler, guard the submit result before branching:
const submit = await transport.submit(...)
if (!submit || (submit.imageUrls == null && submit.taskId == null)) {
throw new Error(`submit response malformed: ${JSON.stringify(submit)}`)
} Type guard
function hasSubmitResult(s: unknown): s is { taskId?: string; imageUrls?: string[] } {
return typeof s === 'object' && s !== null && ('taskId' in s || 'imageUrls' in s)
} Try / catch
try {
await generateImageViaJob(payload)
} catch (e) {
if (e instanceof Error && /returned neither imageUrls nor a taskId/.test(e.message)) {
// vendor-shape failure: log the raw modelId and surface to the user; do NOT auto-retry
notifyUser(`Image model '${modelId}' returned an unexpected response. Check the provider config.`)
} else throw e
} Prevention
- Keep transport adapters under tests that assert submit() returns at least one of imageUrls/taskId for sample vendor responses.
- Treat any vendor error envelope returned with HTTP 200 as a thrown error inside submit(), not a silent empty result.
- Pin the vendor API version in the endpoint and review changelogs before bumping the SDK config modelId.
When it happens
Trigger: The vendor's image-generation submit endpoint replies with an unexpected body shape: an error envelope sent with HTTP 200 (e.g. `{"error":{"code":...}}` or `{"message":"..."}`), a success body whose fields the transport adapter maps to different keys, or a quota/auth rejection that did not surface as a non-2xx status. Also fires when a newly-added transport returns `{}` because its response parser was never wired to populate either field.
Common situations: Vendor API version bump renames the response fields; the provider's apiKey is invalid and the gateway returns a JSON error with status 200; the model id in the SDK config does not exist on the vendor and the body is an error object; a transport adapter bug leaves both fields undefined after parsing.
Related errors
- Rerank response must contain a results array
- Rerank response results must be objects
- ${provider} returned a task id but does not implement pollin
- Rerank response results must contain numeric index and relev
- Failed to generate image: ${error.message}
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/85b5cc88e3055417.
Report an issue: GitHub.