bug: public key is dropped from the “No key found for public key” log line

这个报错通常出现在用不存在的 public key 访问 Langfuse 公共 API(如 /api/public/projects )时,服务端 logger.error("No key found for public key", publicKey) 中的 publicKey 参数被 win

快速结论:这个报错通常出现在用不存在的 public key 访问 Langfuse 公共 API(如 /api/public/projects)时,服务端 logger.error("No key found for public key", publicKey) 中的 publicKey 参数被 winston 丢弃,日志只剩通用文案,无法定位是哪个 key 被拒绝。优先排查日志格式链是否缺少 winston.format.splat(),以及该调用是否用了字符串作为第二个参数。

适用环境:Langfuse 自托管部署(langfuse-web 服务);日志使用 winston(Issue 隔离复现基于 winston 3.19.0,处于 packages/shared 锁定的 ^3.15.0 范围内);同时覆盖 LANGFUSE_LOG_FORMAT=text 与默认 JSON 两种日志格式。Issue 未提供操作系统、Python、CUDA、显卡等信息。

最快修复方案:暂无确认的一步修复方案。Issue 中的维护者确认已复现该问题,并给出修改建议(将字符串插值进日志消息,而不是作为第二个参数传入),但截至 Issue 关闭时没有合并已验证的补丁。

注意事项:建议修复方案来自维护者在讨论中的回复,属于建议性质,未在官方版本中验证;自行改动 apiAuth.ts 属于本地补丁,升级 Langfuse 时需重新检查。此外把 public key 明文写入日志本身涉及信息暴露,落地时需评估日志访问权限。

问题场景

在自托管的 Langfuse 实例上,用不存在的 Basic auth 凭据调用任意公共 API 端点,例如:

curl -s -u "pk-lf-does-not-exist:sk-lf-does-not-exist" https://<host>/api/public/projects

此时查看 langfuse-web 日志,只能看到 error No key found for public key,看不到被拒绝的 public key 具体是什么。对于重复请求,服务端在 API_KEY_NON_EXISTENT 负缓存路径上直接短路,除通用的 Error verifying auth header: Invalid credentials 外不再产生任何额外信号。

报错原文

error  No key found for public key

对应的源码调用(Issue 中引用的 apiAuth.ts):

logger.error("No key found for public key", publicKey);

Issue 标题中的核心英文报错:

bug: public key is dropped from the "No key found for public key" log line

原因分析

Issue 正文和维护者回复都确认了根因:publicKey 是字符串,winston 会把非对象的第二个参数路由到 SPLAT 符号,而不是合并进 info 对象。

  • packages/shared/src/server/logger.ts 的格式链(text 与 json 两条路径)中都没有 winston.format.splat()。
  • 日志消息里也没有 %s 占位符。

因此该值在两种格式下都不会出现在输出中:

  • LANGFUSE_LOG_FORMAT=text:printf 只输出 timestamp、level、message(以及 stack),丢弃所有元数据。
  • 默认 JSON:winston.format.json() 只序列化可枚举的字符串键,而 SPLAT 是 symbol。

对照之下,同一文件中错误密钥(public key 存在但 secret 不匹配)的分支使用了模板字符串插值,能正常打出 key:

logger.debug(`Old key is invalid: ${publicKey}`);

同样调用形态在 apiAuth.ts 中还出现三处,丢弃的值相同:logger.info("No project id found for key", publicKey)、logger.error("Invalid plan type for key", finalApiKey.plan)、logger.info("No api key found for public key:", publicKey)。(维护者在当前 main 上定位到的对应行为 L126、L164、L171、L284。)而 logger.error("...", error) 这类调用传入的是 Error 对象,winston 能正常合并,并由 format.errors({ stack: true }) 打出堆栈,不受此问题影响。

环境排查

  • 确认 Langfuse 部署方式:自托管,涉及 langfuse-web 服务。
  • 确认日志格式:LANGFUSE_LOG_FORMAT=text 还是默认 JSON;两种格式下该问题都存在,需确认自己看的是哪条格式链。
  • 确认产品版本/代码版本:Issue 讨论基于 main commit 471e150a,正文引用行为 apiAuth.ts L116/L154/L161/L314,维护者在 main 上定位为 L126/L164/L171/L284,行号随版本漂移,需以实际版本为准。
  • 确认 winston 版本:Issue 隔离复现使用 3.19.0,处于 packages/shared 锁定的 ^3.15.0 范围内。
  • 确认 packages/shared/src/server/logger.ts 的格式链中是否存在 winston.format.splat()。
  • 确认是否具备 ingress/代理层请求日志:Issue 指出识别配置错误的客户端通常依赖这一层,而许多自托管部署在该主机上没有。
  • Issue 未提供操作系统、Python、CUDA、显卡信息,无需排查。

解决步骤

  1. 先复现:用不存在的 Basic auth 凭据请求任一公共 API 端点,然后在 langfuse-web 日志中确认是否只出现 No key found for public key 而没有 key 值。
  2. 定位触发点:在对应版本的 web/src/features/public-api/server/apiAuth.ts 中找到 logger.error("No key found for public key", publicKey) 这一行(Issue 正文为 L116,main 471e150a 为 L126)。
  3. 核对日志格式链:检查 packages/shared/src/server/logger.ts 中 text 与 json 两条链是否包含 winston.format.splat(),并确认消息中没有 %s 占位符。这是判断根因是否成立的关键。
  4. 可优先尝试维护者在 Issue 中给出的建议修复:把这四处调用改成字符串插值,与已有的 logger.debug(`Old key is invalid: ${publicKey}`) 风格保持一致,例如把 logger.error("No key found for public key", publicKey) 改为 logger.warn(`No key found for public key: ${publicKey}`),并同步处理另外三处(No project id found for key、Invalid plan type for key、No api key found for public key:)。
  5. 关于日志级别:Issue 讨论认为该场景是未知客户端的 401,不是服务端故障,warn 比 error 更合适,与已使用 info 的 No api key found for public key: 一致。此点同样属于建议,落地前自行评估。
  6. 改动范围限定在 web/src/features/public-api/server/apiAuth.ts 上述若干行;Issue 中称可以据此提 PR,但关闭时并未确认已合并的官方修复。
  7. 若暂不修改代码,可退而求其次在 ingress/反向代理层开启请求级日志,用请求头中的 public key 反查,弥补应用日志缺失的信息。

验证方法

应用改动后,重新用不存在的凭据调用公共 API:

curl -s -u "pk-lf-does-not-exist:sk-lf-does-not-exist" https://<host>/api/public/projects

在 langfuse-web 日志中应能看到被拒绝的 public key(例如 No key found for public key: pk-lf-does-not-exist)。请在 LANGFUSE_LOG_FORMAT=text 与默认 JSON 两种格式下分别确认,因为 Issue 指出该值在两种格式下都会丢失。若日志中仍未出现 key,说明改动未生效或日志格式链并非预期的那一条,需要回查第 3 步。

参考来源

langfuse/langfuse #16374

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 25531

发表回复

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