CherryHQ/cherry-studio · error · NonVisionImageOcrError
The selected model doesn't support images, and Cherry Studio
Error message
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.
What it means
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.
Source
Thrown at src/main/ai/messages/attachmentRouting.ts:230
const displayName = ref?.displayName ?? handle
try {
const bareExt = ((await application.get('FileManager').getById(fileEntryId)).ext ?? '').toLowerCase()
const fileType = getFileTypeByExt(bareExt)
if (isNative(bareExt, fileType, ctx.nativeSupport)) {
if (!(await inlineNative(part))) {
logger.warn('Native file materialization failed; degrading to note', { messageId: message.id, displayName })
kept.push(noteOf(handle) as UIMessage['parts'][number])
}
continue
}
// Non-vision image → OCR text when it finds any. If OCR cannot produce
// text, stop before opening a provider request: sending the native image
// to a known non-vision model would only produce a deterministic API error.
if (fileType === FILE_TYPE.IMAGE) {
const ocrText = await ocrNonVisionImage(fileEntryId, ctx.signal)
if (ocrText === null) throw new NonVisionImageOcrError()
const text = `Attached file "${handle}":\n${capInlineText(handle, ocrText, ctx.isToolCapable, ctx.cap)}`
kept.push({ type: 'text', text } as UIMessage['parts'][number])
continue
}
// Non-native first-party attachment → inline its (capped) text.
const body = await extractNonNativeText(fileEntryId, bareExt, fileType, handle, ctx.signal)
const text = `Attached file "${handle}":\n${capInlineText(handle, body, ctx.isToolCapable, ctx.cap)}`
kept.push({ type: 'text', text } as UIMessage['parts'][number])
} catch (error) {
if (ctx.signal?.aborted || isAbortError(error)) throw error
if (error instanceof NonVisionImageOcrError) throw error
logger.error('Failed to prepare attached file', error as Error, { messageId: message.id, displayName })
kept.push(noteOf(handle) as UIMessage['parts'][number])
}
}
return { ...message, parts: kept } as TView on GitHub (pinned to 726446b54c)
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.
Defensive patterns
Strategy: fallback
Validate before calling
// Before sending, route attachments by model capability.
function classifyImageAttachment(modelSupportsVision: boolean, ocrAvailable: boolean, imageHasText: boolean | null) {
if (modelSupportsVision) return 'native' // send the image as-is
if (ocrAvailable && imageHasText) return 'ocr-text' // inline extracted text
return 'blocked' // NonVisionImageOcrError will fire
} Type guard
// Detect the error by name/i18nKey when it propagates:
const isNonVisionImageOcrError = (e: unknown): boolean =>
e instanceof Error && (e.name === 'NonVisionImageOcrError' ||
(e as any).i18nKey === 'image_unreadable_for_non_vision_model') Try / catch
try {
await prepareChatMessages(messages, ctx)
} catch (e) {
if (isNonVisionImageOcrError(e)) {
// surface the i18nKey to the user; offer: switch to a vision model, remove the image, or enable OCR
} else if (isAbortError(e)) {
throw e
}
// other attachment failures degrade to a note inside prepareChatMessages and do not reach here
} Prevention
- 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.
When it happens
Trigger: Sending an image attachment to a model whose capabilities exclude vision, while OCR is unavailable/disabled or yields no text from that image.
Common situations: 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.
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/33faaa6ae3f4a6f6.
Report an issue: GitHub.