{"record":{"id":"85b5cc88e3055417","repo":"CherryHQ/cherry-studio","slug":"openai-compatible-reranking-model-only-supports-te","errorCode":null,"errorMessage":"OpenAI-compatible reranking model only supports text documents","messagePattern":"OpenAI-compatible reranking model only supports text documents","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ai-sdk-provider/src/openai-compatible-reranking-model.ts","lineNumber":52,"sourceCode":"type OpenAICompatibleRerankResponse = {\n  results?: OpenAICompatibleRerankResponseItem[]\n}\n\nexport class OpenAICompatibleRerankingModel implements RerankingModelV3 {\n  readonly specificationVersion = 'v3'\n\n  constructor(\n    readonly modelId: string,\n    private readonly config: OpenAICompatibleRerankingModelConfig\n  ) {}\n\n  get provider(): string {\n    return this.config.provider\n  }\n\n  async doRerank({ documents, headers, query, topN, abortSignal }: DoRerankOptions): Promise<DoRerankResult> {\n    if (documents.type !== 'text') {\n      throw new Error('OpenAI-compatible reranking model only supports text documents')\n    }\n\n    const { value, rawValue } = await postJsonToApi({\n      url: this.config.url({ path: '/rerank', modelId: this.modelId }),\n      headers: combineHeaders(this.config.headers(), headers),\n      body: {\n        model: this.modelId,\n        query,\n        documents: documents.values,\n        top_n: topN\n      },\n      failedResponseHandler: createStatusCodeErrorResponseHandler(),\n      successfulResponseHandler: async ({ response }) => {\n        const rawValue = await response.json()\n        return {\n          value: parseRerankResponse(rawValue, documents.values.length),\n          rawValue\n        }","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ai-sdk-provider/src/openai-compatible-reranking-model.ts#L34-L70","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: transport parses only the happy-path fields\nsubmit(input) {\n  const body = await fetchJson(url, req)\n  return { imageUrls: body.data?.map(d => d.url) }\n}\n// after: surface non-2xx / error envelopes so submit() never returns a bare {}\nsubmit(input) {\n  const body = await fetchJson(url, req)\n  if (body.code || body.error) {\n    throw new Error(`vendor rejected submit: ${body.message ?? JSON.stringify(body)}`)\n  }\n  return { imageUrls: body.data?.map(d => d.url), taskId: body.task_id }\n}","handlingStrategy":"validation","validationCode":"// Before invoking the job, assert the transport is one the registry knows\n// and that submit() cannot return a bare object. Validate at adapter\n// registration time so a malformed submit fails loudly in tests, not in prod.\nfunction assertTransportContract(t: ImageGenerationTransport) {\n  if (typeof t.submit !== 'function') throw new Error('transport.submit missing')\n  // optional poll, but if absent the adapter MUST guarantee sync imageUrls\n}\n// In the job handler, guard the submit result before branching:\nconst submit = await transport.submit(...)\nif (!submit || (submit.imageUrls == null && submit.taskId == null)) {\n  throw new Error(`submit response malformed: ${JSON.stringify(submit)}`)\n}","typeGuard":"function hasSubmitResult(s: unknown): s is { taskId?: string; imageUrls?: string[] } {\n  return typeof s === 'object' && s !== null && ('taskId' in s || 'imageUrls' in s)\n}","tryCatchPattern":"try {\n  await generateImageViaJob(payload)\n} catch (e) {\n  if (e instanceof Error && /returned neither imageUrls nor a taskId/.test(e.message)) {\n    // vendor-shape failure: log the raw modelId and surface to the user; do NOT auto-retry\n    notifyUser(`Image model '${modelId}' returned an unexpected response. Check the provider config.`)\n  } else throw e\n}","preventionTips":["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."],"tags":["image-generation","custom-provider","vendor-api","transport"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}