Misc. bug: validation errors on /v1/chat/completions with X-Conversation-Id return 500 bad_function_call instead of 400

用户在 llama-server 中通过 /v1/chat/completions 接口发送带有 X-Conversation-Id 头部的请求时触发。该头部用于启用可恢复流(resumable-stream)。任何请求体参数校验失败(如 backend_sampling 字段为字符串而非布尔值)都

Misc. bug: validation errors on /v1/chat/completions with X-Conversation-Id return 500 bad_function_call instead of 400

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 是否为布尔值而非空字符串。

解决步骤

  1. 方案一(推荐,优先尝试):更新 llama.cpp 至包含此修复的版本。修复提交在 tools/server/server-stream.cppserver_res_spipe::on_complete() 中添加了 !next_orig 守卫检查,并在 tools/ui/src/lib/stores/chat.svelte.ts 中对 backend_sampling 字段添加了 hasValue() 守卫过滤。
  2. 方案二(手动修复):
    1. 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;
      }
    2. 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;
    3. 重新编译并重启 llama-server。
  3. 临时规避方法:
    1. 在 webui 设置中切换 backend_sampling 开关,使其存储正确的布尔值。
    2. 在 API 请求中移除 X-Conversation-Id 头部,但会失去流恢复功能。

验证方法

使用 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,...}

同时确认正常流式聊天请求(无校验失败)仍可正常工作。

参考来源

ggml-org/llama.cpp #25605

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

celebrityanime
celebrityanime
文章: 14580

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注