
Misc. bug: validation errors on /v1/chat/completions with X-Conversation-Id return 500 bad_function_call instead of 400
快速结论:该报错发生在 llama-server 处理带 X-Conversation-Id 头部的 POST /v1/chat/completions 请求时,若请求体参数校验失败,服务器错误地返回 HTTP 500 并附带 bad_function_call 纯文本消息,而非正确的 400 JSON 错误响应。优先排查是否使用了 bundled webui 的默认设置(backend_sampling 字段为空字符串),或检查 llama.cpp 版本是否低于包含修复的提交。
问题场景
用户在 llama-server 中通过 /v1/chat/completions 接口发送带有 X-Conversation-Id 头部的请求时触发。该头部用于启用可恢复流(resumable-stream)。任何请求体参数校验失败(如 backend_sampling 字段为字符串而非布尔值)都会触发此问题。bundled webui 的新用户首次使用时会自动触发,因为 webui 的默认设置会将 backend_sampling 发送为空字符串。
报错原文
HTTP 500
bad_function_call
Server log:
E srv operator(): got exception: bad_function_call
正常校验失败应返回:
HTTP 400
{"error":{"code":400,"message":"Field 'backend_sampling': [json.exception.type_error.302] type must be boolean, but is string","type":"invalid_request_error"}}
原因分析
根本原因:在 tools/server/server-stream.cpp 中,server_res_spipe::set_req() 在解析请求体之前就附接了流管道(spipe)。当参数校验抛出异常时,server_res_spipe::set_next() 从未被调用,导致 next_orig 保持为空的 std::function。错误处理路径仍会调用 on_complete(),其守卫检查 !spipe || next_finished 均为 false,于是尝试在空函数上调用 next_orig(chunk),抛出 std::bad_function_call 异常。set_exception_handler 将其转换为纯文本的 500 响应,覆盖了已准备好的 400 JSON 响应体。
附加问题:webui 中的 backend_sampling 字段没有通过 hasValue() 守卫过滤空字符串占位符,导致默认设置下的每个请求都包含无效的 "" 值。
环境排查
- llama.cpp 版本:在 b9978 (0c4fa7a989) 中确认存在该问题;建议升级至包含修复的版本。
- 操作系统:Linux x86_64(已验证);该 bug 与后端无关。
- 模型:任何模型均可复现,因为请求在推理前已失败。
- 相关头部:确认请求中是否包含
X-Conversation-Id。 - webui 设置:检查
backend_sampling是否为布尔值而非空字符串。
解决步骤
- 方案一(推荐,优先尝试):更新 llama.cpp 至包含此修复的版本。修复提交在
tools/server/server-stream.cpp的server_res_spipe::on_complete()中添加了!next_orig守卫检查,并在tools/ui/src/lib/stores/chat.svelte.ts中对backend_sampling字段添加了hasValue()守卫过滤。 - 方案二(手动修复):
- 在
tools/server/server-stream.cpp中修改server_res_spipe::on_complete()函数:将if (!spipe || next_finished)扩展为if (!spipe || next_finished || !next_orig),并添加会话清理逻辑:if (!next_orig) { g_stream_sessions.evict(server_stream_conv_id_from_headers(req->headers)); return; } - 在
tools/ui/src/lib/stores/chat.svelte.ts中将apiOptions.backend_sampling = currentConfig.backend_sampling;替换为:if (hasValue(currentConfig.backend_sampling)) apiOptions.backend_sampling = currentConfig.backend_sampling; - 重新编译并重启 llama-server。
- 在
- 临时规避方法:
- 在 webui 设置中切换
backend_sampling开关,使其存储正确的布尔值。 - 在 API 请求中移除
X-Conversation-Id头部,但会失去流恢复功能。
- 在 webui 设置中切换
验证方法
使用 curl 发送带 X-Conversation-Id 头部的无效请求,确认返回 HTTP 400 并附带 JSON 错误消息:
curl -sw '\n%{http_code}\n' http://127.0.0.1:8081/v1/chat/completions \
-H 'Content-Type: application/json' -H 'X-Conversation-Id: test' \
-d '{"messages":[{"role":"user","content":"m"}],"backend_sampling":""}'
预期输出:HTTP 400 状态码,响应体为 {"error":{"code":400,...}。
同时确认正常流式聊天请求(无校验失败)仍可正常工作。



