diff --git a/src/services/api/errors.test.ts b/src/services/api/errors.test.ts index 92508240..0d49bb11 100644 --- a/src/services/api/errors.test.ts +++ b/src/services/api/errors.test.ts @@ -7,21 +7,18 @@ import { describe('image unsupported API errors', () => { test('detects provider-specific text-only model image rejections', () => { - expect( - isUnsupportedImageInputErrorMessage( - 'This model does not support image blocks', - ), - ).toBe(true) - expect( - isUnsupportedImageInputErrorMessage( - 'unsupported modality: image input is not available', - ), - ).toBe(true) - expect( - isUnsupportedImageInputErrorMessage( - 'Failed to deserialize the JSON body into the target type: messages[1]: unknown variant `image_url`, expected `text` at line 1 column 394097', - ), - ).toBe(true) + const unsupportedImageErrors = [ + 'This model does not support image blocks', + 'unsupported modality: image input is not available', + 'Failed to deserialize the JSON body into the target type: messages[1]: unknown variant `image_url`, expected `text` at line 1 column 394097', + "Invalid value for 'messages[0].content[1].type': 'image_url' is not one of ['text']", + "messages.0.content.1.type: Input should be 'text'; received 'image_url'", + 'image_url content parts are not allowed for this model', + ] + + for (const message of unsupportedImageErrors) { + expect(isUnsupportedImageInputErrorMessage(message)).toBe(true) + } expect(isUnsupportedImageInputErrorMessage('image exceeds maximum')).toBe(false) }) diff --git a/src/services/api/errors.ts b/src/services/api/errors.ts index a77459c6..1b8708eb 100644 --- a/src/services/api/errors.ts +++ b/src/services/api/errors.ts @@ -430,7 +430,7 @@ export function extractUnknownErrorFormat(value: unknown): string | undefined { export function isUnsupportedImageInputErrorMessage(message: string): boolean { const raw = message.toLowerCase() if (!raw.includes('image')) return false - if (raw.includes('image_url') && raw.includes('expected') && raw.includes('text')) { + if (isOpenAIImageUrlTextOnlySchemaError(raw)) { return true } return ( @@ -444,6 +444,31 @@ export function isUnsupportedImageInputErrorMessage(message: string): boolean { ) } +function isOpenAIImageUrlTextOnlySchemaError(raw: string): boolean { + if (!raw.includes('image_url')) return false + if ( + raw.includes('not allowed') || + raw.includes('not permitted') || + raw.includes('disallowed') || + raw.includes('forbidden') + ) { + return true + } + if (!raw.includes('text')) return false + return ( + raw.includes('expected') || + raw.includes('input should be') || + raw.includes('not one of') || + raw.includes('permitted') || + raw.includes('received') || + raw.includes('unknown variant') || + raw.includes('invalid value') || + raw.includes('invalid type') || + raw.includes('valid enumeration') || + raw.includes('only text') + ) +} + export function getAssistantMessageFromError( error: unknown, model: string,