StackTraces
Spring AI已驗證

Spring AI image_url must be valid URL or data URI

@debug_master13 天前更新閱讀 6 min00

多模態傳圖報 400,先分清兩種:image_url 既不是公網 URL,也不是 data:image/png;base64,... 這種 data URI;或者你用了個根本不支持視覺的模型。前者用 Spring AI 的 Media 配 Resource 交給框架轉 base64,後者換成 gpt-4o 系列等支持視覺的模型。

Issue & Env(案發現場與環境)

  • 業務場景:用 Spring AI 多模態消息給模型傳圖,請求直接彈 400。
  • 運行環境:Spring AI 1.1.x,OpenAI Chat 接口;多模態圖片走 content 陣列裡的 image_url。
  • 觸發條件:image_url 不是合法 URL 或 data URI,或者模型不支持圖片輸入。

StackTrace(堆疊日誌)

接口返回的第一種形態,值本身不合法:

Error code: 400 - {'error': {'message': 'Invalid content type. image_url must be a valid URL or data URI.', 'type': 'invalid_request_error', 'param': 'messages[0].content[1].image_url', 'code': None}}

第二種形態,模型不支持視覺:

Error code: 400 - {'error': {'message': "Unsupported value: 'image_url' is not supported with this model.", 'type': 'invalid_request_error', 'param': 'messages[0].content[1]', 'code': 'unsupported_value'}}

這兩條是 OpenAI 視覺接口的常見報錯形態,具體文案以實際接口返回為準。落到 Spring AI 側,它會把它包成不可重試例外往外拋,因為 4xx 預設不重試。

Reproduce(復現程式碼)

最典型的錯法,把本地文件路徑當成 URL:

String response = ChatClient.create(chatModel).prompt()
    .user(u -> u.text("描述这张图片")
        .media(MimeTypeUtils.IMAGE_PNG, "file:///Users/me/pic.png"))
    .call()
    .content();

或者手工拼 base64 時漏了前綴:

String b64 = Base64.getEncoder()
    .encodeToString(Files.readAllBytes(Path.of("/tmp/pic.png")));
// 少了 data:image/png;base64, 前缀,接口认不出来

Root Cause(根因分析)

image_url 這個字段只認兩種值:公網可訪問的 http(s) URL,或 data:image/xxx;base64,... 形式的 data URI。本地文件路徑、file://、內網地址、裸 base64 字符串都不合法,校驗階段直接 400。

用 Media 傳 Resource 時,Spring AI 會讀資源,並按 MIME 類型轉成 data URI。所以 ClassPathResource、FileSystemResource 都能用,框架替你補了前綴和編碼。你要是手動拼 base64,卻忘了加 data:image/png;base64,,就等於把一個裸字符串塞進 image_url,報的正是第一條。

第二條是另一個維度的事:模型不支持視覺輸入。像 gpt-3.5 系列就不接受圖片,請求裡出現 image_url 會被判 unsupported_value。這跟圖片合不合法無關,純粹是模型選錯了。

還有個常被忽略的成本點:圖片進上下文會按 token 計費。大圖轉 base64 後請求體動輒幾 MB,既拖慢請求,也抬高 token 消耗。

The Fix(解決方案)

交給框架:Media + MimeTypeUtils + Resource

首選這條,讓 Spring AI 自己讀文件、轉 data URI:

String response = ChatClient.create(chatModel).prompt()
    .user(u -> u.text("描述这张图片")
        .media(MimeTypeUtils.IMAGE_PNG, new ClassPathResource("/images/pic.png")))
    .call()
    .content();

磁盤文件用 FileSystemResource,類路徑資源用 ClassPathResource。別把路徑字符串直接當 URL 傳。

手工拼 base64 別漏前綴

確實要自己編碼時,data URI 前綴必須帶上:

String dataUri = "data:image/png;base64," + b64;

MIME 類型要和圖片真實格式一致,png 配 image/png,jpg 配 image/jpeg。

換成支持視覺的模型

先確認模型的輸入模態裡有圖像。gpt-4o 系列支持視覺,gpt-3.5 系列不支持。別拿純文本模型試圖片。

大圖先壓縮

base64 會讓體積漲約三分之一,再加上按 token 計費,傳原圖很虧。先縮放、裁剪再傳。真需要原圖細節,就用支持 URL 的方式,讓服務端自己去拉取。

References(參考來源)

最後更新於 2026-09-28

這篇幫到你了嗎?

剛解決了一個棘手的報錯?花兩分鐘記錄下來,幫助下一個遇到同樣問題的開發者。

貢獻一條解法