快速结论:这个报错通常出现在 llama.cpp server 已启动多模态(--mmproj)时:客户端读取了 GET /props 返回的随机 media_marker,又把该响应内容作为普通用户文本回传,导致服务端在渲染后的 prompt 中扫描到的媒体标记数量与随附的 bitmap 数量不一致,从而返回 400。优先排查调用链里是否把 /props 响应原样写进了对话历史或工具输出。
适用环境:Issue 中确认的环境为 ghcr.io/ggml-org/llama.cpp:server-cuda-v0.4.1、build_info b10964-b29c606e2、libmtmd.so.0.4.1,并以 --mmproj 启动。原 Issue 未提供操作系统、Python、CUDA 版本或具体显卡型号。
最快修复方案:暂无确认的一步修复方案。Issue 以 duplicate 关闭并指向 #28249,其中说明根因是渲染后的 prompt 在解析媒体标记与特殊 token 时未区分用户输入内容;因此可优先尝试的规避方式是:不要让 /props 的响应进入会话历史或 prompt,或确保一次请求中“文本里的 marker 数量”与“实际附带的 bitmap 数量”严格一致。不要把它当成已验证的最终修复。
注意事项:Issue 中 /props 会把当前进程的 marker 发给任意未认证调用方,且该 marker 在进程生命周期内保持稳定,所以一旦进入 transcript,每次重放都会复现;服务端返回的是被折叠后的通用错误,难以直接定位到 marker 计数问题。该 Issue 已被关闭为 #28249 的重复项,以上规避措施属于可优先尝试,并非官方确认修复。
问题场景
用户运行 llama.cpp server(CUDA 容器镜像),以 --mmproj 启动多模态能力,然后通过 HTTP API 调用 /v1/chat/completions。典型触发路径是 LLM agent 或客户端先调用 GET /props 做能力发现,把返回的 media_marker 当作普通文本保存为工具输出,随后每一轮都把这段文本重新发回服务端。此时即使请求里带了真实图片,也会因为渲染后的 prompt 中 marker 数量多于 bitmap 数量而失败。
报错原文
400 {"error":{"code":400,"message":"Failed to tokenize prompt","type":"invalid_request_error"}}
number of media markers in text (%zu) does not match number of bitmaps (%zu)
原因分析
最可能的原因是 mtmd_tokenize 在渲染后的 prompt 文本中统计媒体标记数量,并要求该数量等于随请求附带的 bitmap 数量;而 get_media_marker() 不做验证或转义,随机化本身是唯一防线。PR #21962 的随机化防的是“意外碰撞”,但 GET /props 会把当前进程的 marker 公开给未认证调用方,任何读取并回放该响应的客户端都会“必然”碰撞。服务端随后把具体的 mtmd 错误折叠成通用的 Failed to tokenize prompt,导致诊断成本很高。原 Issue 还指出,文本中 marker 与真实图片同时出现时(2 个 marker vs 1 个 bitmap)同样返回 400,说明这不只是纯文本轮次的问题。
环境排查
- 确认 llama.cpp server 是否以
--mmproj启动,且容器/构建版本为ghcr.io/ggml-org/llama.cpp:server-cuda-v0.4.1、build_info b10964-b29c606e2、libmtmd.so.0.4.1这类包含 mtmd 的版本;Issue 未确认其他版本是否受影响。 - 确认客户端调用链中是否存在读取
/props并把返回内容写入 prompt、工具输出或对话历史的行为。 - 确认失败请求中文本部分是否包含当前服务进程的
media_marker,以及该请求实际附带的 bitmap 数量。 - 确认是否处于负载均衡或 failover 之后;复现时同一段文本在不同 server 上可能表现不同,容易被误判为间歇性故障。
- 确认排查时
/health、/v1/models、/slots、/metrics、/apply-template以及 400 错误体都不包含 marker,只有/props会泄漏。
解决步骤
- 先复现并确认 marker 来源。原 Issue 的复现方式是用
curl -s http://SERVER:PORT/props | jq -r .media_marker取出 marker,再把它作为 user content 发送到/v1/chat/completions,应得到上述 400。 - 检查客户端/agent 的工具输出与历史管理逻辑,找出把
/props响应内容持久化并回放的地方。若确实存在,优先阻止该响应进入 prompt 或 transcript。 - 如果必须调用
/props做能力发现,可优先尝试只提取所需字段(例如模型名、能力标志),不要把整个响应体或media_marker字段内容写入对话历史。 - 对于视觉请求,确保渲染后文本中出现的媒体标记数量与请求实际附带的 bitmap 数量一致;原 Issue 测得“真实图片 + 文本中 marker”会变成 2 个 marker vs 1 个 bitmap 并返回 400。
- 若仍触发,记录失败请求的文本内容、附带图片数量、服务端启动参数和 llama.cpp 版本,作为向 #28249 反馈的依据;该 Issue 的根因处理指向渲染 prompt 中对用户文本的媒体标记/特殊 token 解析问题。
验证方法
用同一段曾触发 400 的文本重新发送请求:如果不再出现 Failed to tokenize prompt,且不包含 stray marker 的纯文本请求返回正常、带真实图片且无多余 marker 的请求返回 200,说明规避生效。也可用原 Issue 的对照思路验证:改用随机不同字符或使用 pre-#21962 的默认 <__media__> 时应返回 200,而包含当前进程 marker 的请求返回 400,以此确认触发条件是否仍存在。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


