{"record":{"id":"33faaa6ae3f4a6f6","repo":"CherryHQ/cherry-studio","slug":"the-selected-model-doesn-t-support-images-and-che","errorCode":null,"errorMessage":"The selected model doesn't support images, and Cherry Studio couldn't extract readable text from the attachment. Choose a vision-capable model or remove the image and try again.","messagePattern":"The selected model doesn't support images, and Cherry Studio couldn't extract readable text from the attachment\\. Choose a vision-capable model or remove the image and try again\\.","errorType":"exception","errorClass":"NonVisionImageOcrError","httpStatus":null,"severity":"error","filePath":"src/main/ai/messages/attachmentRouting.ts","lineNumber":230,"sourceCode":"    const displayName = ref?.displayName ?? handle\n    try {\n      const bareExt = ((await application.get('FileManager').getById(fileEntryId)).ext ?? '').toLowerCase()\n      const fileType = getFileTypeByExt(bareExt)\n\n      if (isNative(bareExt, fileType, ctx.nativeSupport)) {\n        if (!(await inlineNative(part))) {\n          logger.warn('Native file materialization failed; degrading to note', { messageId: message.id, displayName })\n          kept.push(noteOf(handle) as UIMessage['parts'][number])\n        }\n        continue\n      }\n\n      // Non-vision image → OCR text when it finds any. If OCR cannot produce\n      // text, stop before opening a provider request: sending the native image\n      // to a known non-vision model would only produce a deterministic API error.\n      if (fileType === FILE_TYPE.IMAGE) {\n        const ocrText = await ocrNonVisionImage(fileEntryId, ctx.signal)\n        if (ocrText === null) throw new NonVisionImageOcrError()\n        const text = `Attached file \"${handle}\":\\n${capInlineText(handle, ocrText, ctx.isToolCapable, ctx.cap)}`\n        kept.push({ type: 'text', text } as UIMessage['parts'][number])\n        continue\n      }\n\n      // Non-native first-party attachment → inline its (capped) text.\n      const body = await extractNonNativeText(fileEntryId, bareExt, fileType, handle, ctx.signal)\n      const text = `Attached file \"${handle}\":\\n${capInlineText(handle, body, ctx.isToolCapable, ctx.cap)}`\n      kept.push({ type: 'text', text } as UIMessage['parts'][number])\n    } catch (error) {\n      if (ctx.signal?.aborted || isAbortError(error)) throw error\n      if (error instanceof NonVisionImageOcrError) throw error\n      logger.error('Failed to prepare attached file', error as Error, { messageId: message.id, displayName })\n      kept.push(noteOf(handle) as UIMessage['parts'][number])\n    }\n  }\n\n  return { ...message, parts: kept } as T","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/messages/attachmentRouting.ts#L212-L248","documentation":"Intentional, user-facing error from the chat attachment router. When a non-vision model is sent an image and OCR (`ocrNonVisionImage`) returns `null` — meaning no readable text could be extracted (OCR unconfigured, failed, or image has no text) — the router throws `NonVisionImageOcrError` to STOP before the provider call, avoiding a deterministic provider-side 'model does not support images' error. The class carries `i18nKey = 'image_unreadable_for_non_vision_model'` for localization, and the prepareChatMessage catch re-throws it (it is NOT degraded to a note) so it surfaces to the user. Abort errors are re-thrown first.","triggerScenarios":"Sending an image attachment to a model whose capabilities exclude vision, while OCR is unavailable/disabled or yields no text from that image.","commonSituations":"User attaches a screenshot to a text-only model with OCR off or OCR service misconfigured; the image is purely graphical (no text); OCR API key/endpoint missing.","solutions":["Switch to a vision-capable model for this conversation.","Remove the image and re-send, or attach a text excerpt instead.","Enable/configure OCR so non-vision models can fall back to extracted text.","If the image contains text, ensure OCR is reachable and retry."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Before sending, route attachments by model capability.\nfunction classifyImageAttachment(modelSupportsVision: boolean, ocrAvailable: boolean, imageHasText: boolean | null) {\n  if (modelSupportsVision) return 'native'           // send the image as-is\n  if (ocrAvailable && imageHasText) return 'ocr-text' // inline extracted text\n  return 'blocked'                                    // NonVisionImageOcrError will fire\n}","typeGuard":"// Detect the error by name/i18nKey when it propagates:\nconst isNonVisionImageOcrError = (e: unknown): boolean =>\n  e instanceof Error && (e.name === 'NonVisionImageOcrError' ||\n    (e as any).i18nKey === 'image_unreadable_for_non_vision_model')","tryCatchPattern":"try {\n  await prepareChatMessages(messages, ctx)\n} catch (e) {\n  if (isNonVisionImageOcrError(e)) {\n    // surface the i18nKey to the user; offer: switch to a vision model, remove the image, or enable OCR\n  } else if (isAbortError(e)) {\n    throw e\n  }\n  // other attachment failures degrade to a note inside prepareChatMessages and do not reach here\n}","preventionTips":["Gate image attachments on the model's vision capability before sending.","Keep OCR configured and reachable so non-vision models can fall back to text.","Offer a vision-capable model in the UI when an image is attached.","Distinguish this pre-provider guard from a provider-side image rejection — only the former carries the i18nKey."],"tags":["attachments","ocr","vision","user-facing","pre-provider-guard"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}