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

这篇帮到你了吗?

刚解决了一个棘手的报错?花两分钟记录下来,帮助下一个遇到同样问题的开发者。

贡献一条解法