Skip to content

fix: show vision-specific error when provider returns 404 for image requests#1187

Open
currentsuspect wants to merge 5 commits into
Gitlawb:mainfrom
currentsuspect:fix/vision-not-supported-error-hint
Open

fix: show vision-specific error when provider returns 404 for image requests#1187
currentsuspect wants to merge 5 commits into
Gitlawb:mainfrom
currentsuspect:fix/vision-not-supported-error-hint

Conversation

@currentsuspect
Copy link
Copy Markdown

@currentsuspect currentsuspect commented May 15, 2026

Summary

When a user sends images to a provider/model that doesn't support vision (e.g., MiMo V2.5 Pro via opengateway.gitlawb.com), the provider returns HTTP 404. Previously this showed a misleading "verify OPENAI_BASE_URL" message with no mention of images.

Now detects image content in the request body and classifies the 404 as vision_not_supported, showing a clear message: the model may not support image/vision inputs, with a suggestion to remove images or switch to a vision-capable model.

Also moves the Accept-Encoding: identity header for the Opengateway gateway from transportConfig.headers (model discovery only) to transportConfig.openaiShim.headers (used for chat/completions requests), ensuring the header is actually sent on API calls.

Changes

  • openaiErrorClassification.ts: Added vision_not_supported failure category and detection logic — when a 404 occurs and the request contains image content, it's classified as vision-not-supported instead of generic endpoint-not-found
  • openaiShim.ts: Passes hasImages flag to classifyOpenAIHttpFailure via a bodyContainsImages() helper that inspects structured message content blocks (type: 'image_url' for chat completions, type: 'input_image' for responses) instead of substring-matching the serialized JSON
  • errors.ts: Added user-facing message for vision_not_supported that names the provider, model, and suggests removing images or switching models
  • gitlawb-opengateway.ts: Moved Accept-Encoding: identity from transportConfig.headers to transportConfig.openaiShim.headers so it is sent on chat/completions requests, not just model discovery
  • Tests: Added tests in openaiErrorClassification.test.ts, errors.openaiCompatibility.test.ts, and a request-capture test in openaiShim.test.ts verifying the Accept-Encoding header is sent

Before / After

Before (confusing):

Provider endpoint at opengateway.gitlawb.com returned 404. Verify OPENAI_BASE_URL is correct and that the selected model (mimo-v2.5-pro) is supported by this provider.

After (actionable):

The provider at opengateway.gitlawb.com returned 404 for a request containing images. The model (mimo-v2.5-pro) may not support image/vision inputs. Try removing images from your message, or /model to a vision-capable model.

Test plan

  • bun test src/services/api/openaiErrorClassification.test.ts — 16 pass
  • bun test src/services/api/errors.openaiCompatibility.test.ts — 5 pass
  • bun test src/services/api/openaiShim.test.ts — 95 pass (including new opengateway header capture test)
  • Manual: paste 2 images with MiMo V2.5 Pro on opengateway.gitlawb.com and verify the new error message appears
  • Manual: verify text-only requests still work normally

🤖 Generated with OpenClaude

Copilot AI review requested due to automatic review settings May 15, 2026 15:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a dedicated error classification for the case where a provider returns HTTP 404 in response to a request that includes image content, replacing the misleading "verify OPENAI_BASE_URL" message with a vision-specific hint that names the model and provider.

Changes:

  • New vision_not_supported failure category in openaiErrorClassification.ts, triggered when status is 404 and the request body included image content.
  • openaiShim.ts passes a hasImages flag (detected via serializedBody.includes('"image_url"')) to classifyOpenAIHttpFailure at all three call sites.
  • New user-facing message in errors.ts plus tests in both classification and compatibility test suites.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/services/api/openaiErrorClassification.ts Adds new category, extends classifier signature with hasImages, and short-circuits 404 responses with images to the new category.
src/services/api/openaiShim.ts Passes hasImages to the classifier from all three HTTP failure sites by sniffing "image_url" in the serialized body.
src/services/api/errors.ts Adds user-facing error text for vision_not_supported, including host, model, and switch suggestion.
src/services/api/openaiErrorClassification.test.ts Adds unit test covering 404 + hasImages: true.
src/services/api/errors.openaiCompatibility.test.ts Adds test verifying the new message contains image, model, and host, and avoids the legacy OPENAI_BASE_URL text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Collaborator

@jatmn jatmn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [P2] Put the compression header where chat requests actually read it
    src/integrations/gateways/gitlawb-opengateway.ts:17
    The new Accept-Encoding: identity header is attached to transportConfig.headers, but OpenAI-compatible chat requests build their headers from the runtime shim config, specifically the descriptor's transportConfig.openaiShim.headers. The top-level transportConfig.headers path is merged for model discovery, not for the _doOpenAIRequest chat/completions path, so MiMo requests still won't send this header and the gzip/ZlibError issue described in the commit remains unfixed. Please either move this header under transportConfig.openaiShim.headers or merge top-level transport headers into the shim runtime config, and add a request-capture test for the Opengateway route.

Copilot AI review requested due to automatic review settings May 15, 2026 18:22
@currentsuspect currentsuspect requested a review from jatmn May 15, 2026 18:24
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Comment on lines +18 to +20
headers: {
'Accept-Encoding': 'identity',
},
Comment on lines +706 to +786
test('opengateway sends Accept-Encoding: identity header on chat requests', async () => {
let capturedHeaders: Headers | undefined

registerGateway({
id: 'gitlawb-opengateway-test',
label: 'Gitlawb Opengateway',
category: 'aggregating',
defaultBaseUrl: 'https://opengateway.gitlawb.com/v1/xiaomi-mimo',
defaultModel: 'mimo-v2.5-pro',
setup: {
requiresAuth: false,
authMode: 'none',
},
transportConfig: {
kind: 'openai-compatible',
openaiShim: {
headers: {
'Accept-Encoding': 'identity',
},
defaultAuthHeader: {
name: 'api-key',
scheme: 'raw',
},
preserveReasoningContent: true,
requireReasoningContentOnAssistantMessages: true,
reasoningContentFallback: '',
maxTokensField: 'max_completion_tokens',
supportsApiFormatSelection: false,
supportsAuthHeaders: false,
},
},
})

process.env.CLAUDE_CODE_USE_OPENAI = '1'
process.env.OPENAI_BASE_URL = 'https://opengateway.gitlawb.com/v1/xiaomi-mimo'
process.env.OPENAI_MODEL = 'mimo-v2.5-pro'

globalThis.fetch = (async (_input, init) => {
capturedHeaders = new Headers(init?.headers)

return new Response(
JSON.stringify({
id: 'chatcmpl-1',
model: 'mimo-v2.5-pro',
choices: [
{
message: {
role: 'assistant',
content: 'ok',
},
finish_reason: 'stop',
},
],
usage: {
prompt_tokens: 8,
completion_tokens: 3,
total_tokens: 11,
},
}),
{
headers: {
'Content-Type': 'application/json',
},
},
)
}) as FetchType

const client = createOpenAIShimClient({}) as OpenAIShimClient

await client.beta.messages.create(
{
model: 'mimo-v2.5-pro',
system: 'test system',
messages: [{ role: 'user', content: 'hello' }],
max_tokens: 64,
stream: false,
},
{},
)

expect(capturedHeaders?.get('Accept-Encoding')).toBe('identity')
Comment thread src/services/api/openaiShim.ts Outdated
status,
body: errorBody,
url: requestUrl,
hasImages: serializedBody.includes('"image_url"'),
@Vasanthdev2004
Copy link
Copy Markdown
Collaborator

Blockers

None.

Non-Blocking

  • PR went through review cycle — jatmn's header placement concern has been addressed in latest commit.

Looks Good

  • Adds vision_not_supported error category for 404 responses with image content
  • Clear user-facing message naming provider, model, and suggesting alternatives
  • Accept-Encoding: identity header now correctly placed under openaiShim.headers
  • 126 tests passing across 3 test files
  • Clean, focused change (159 additions, 0 deletions)

Verdict: Approve — clean error handling improvement.

Vasanthdev2004
Vasanthdev2004 previously approved these changes May 16, 2026
Copy link
Copy Markdown
Collaborator

@Vasanthdev2004 Vasanthdev2004 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean error handling improvement. No blockers.

jatmn
jatmn previously approved these changes May 17, 2026
Copy link
Copy Markdown
Collaborator

@jatmn jatmn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for following up on the earlier review. The requested Accept-Encoding: identity header placement is now under transportConfig.openaiShim.headers, and the added request-capture test covers the chat/completions path that previously missed it.

No issues here, LGTM.

@jatmn
Copy link
Copy Markdown
Collaborator

jatmn commented May 17, 2026

please rebase from main, this should fix smoke issues

gnanam1990
gnanam1990 previously approved these changes May 17, 2026
Copy link
Copy Markdown
Collaborator

@gnanam1990 gnanam1990 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independently verified both parts of this. The vision_not_supported classification is cleanly done — ordered before the generic 404 branch, hasImages plumbed for both the chat and responses transports via bodyContainsImages(), and the message is appropriately hedged with good test coverage. The opengateway Accept-Encoding: identity header is now under transportConfig.openaiShim.headers with a request-capture test, which resolves @jatmn's earlier point.

One non-blocking note for a possible follow-up: the status === 404 && hasImages check precedes the remote-host endpoint_not_found branch, so a genuinely misconfigured base URL/endpoint that happens to send an image would now surface the "model may not support vision" message instead of the /v1 endpoint hint. The wording is hedged so it's not harmful, but worth keeping in mind if endpoint-misconfig reports come in. Approving.

currentsuspect and others added 4 commits May 17, 2026 10:34
…equests

When a user sends images to a provider/model that doesn't support
vision (e.g., MiMo V2.5 Pro via opengateway.gitlawb.com), the provider
returns HTTP 404. Previously this showed a misleading "verify
OPENAI_BASE_URL" message with no mention of images.

Now detects image content in the request body and classifies the 404 as
'vision_not_supported', showing a clear message: the model may not
support image/vision inputs, with a suggestion to remove images or
switch to a vision-capable model.

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
The opengateway.gitlawb.com server returns gzip-compressed responses
by default, which causes ZlibError in the fetch client. Adding
Accept-Encoding: identity header requests uncompressed responses.

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
The Accept-Encoding: identity header was on transportConfig.headers which
is only used for model discovery. Chat/completions requests read headers
from transportConfig.openaiShim.headers. Move it there and add a
request-capture test confirming the header is sent.

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Replace serializedBody.includes('"image_url"') with a bodyContainsImages()
helper that inspects the structured message content blocks. For chat
completions it checks content[].type === 'image_url', for responses it
checks content[].type === 'input_image'. This avoids false positives when
user/tool content happens to contain the literal string "image_url".

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Copilot AI review requested due to automatic review settings May 17, 2026 07:35
@currentsuspect currentsuspect force-pushed the fix/vision-not-supported-error-hint branch from 34b7fca to f07fbf1 Compare May 17, 2026 07:35
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comment on lines +2124 to +2141
const bodyContainsImages = (): boolean => {
if (request.transport === 'responses') {
const responsesBody = buildResponsesBody()
const input = responsesBody.input as Array<Record<string, unknown>> | undefined
if (!Array.isArray(input)) return false
return input.some(item => {
const content = item.content as Array<Record<string, unknown>> | undefined
return Array.isArray(content) && content.some(part => part.type === 'input_image')
})
}
const messages = body.messages as Array<Record<string, unknown>> | undefined
if (!Array.isArray(messages)) return false
return messages.some(msg => {
const content = msg.content
if (!Array.isArray(content)) return false
return content.some((part: Record<string, unknown>) => part.type === 'image_url')
})
}
The helper was inserted between the stableStringify rationale comment and
the serializeBody function it describes, making the comment appear to
document bodyContainsImages. Move it above the comment so the comment
remains adjacent to serializeBody.

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants