
bug: prompt cache key collides version numbers with numeric labels, returning the wrong prompt
快速结论:当 Langfuse 的 Prompt 同时存在版本号 N 和值为 “N” 的自定义标签时,Redis 缓存键会因未区分类型而碰撞,导致按版本查询和按标签查询互相返回错误的 Prompt 版本。优先排查 getCacheKey 中 params.version ?? params.label 的合并逻辑。
问题场景
在自托管或 Langfuse Cloud 环境中,使用 Redis 存储并开启了默认的 Prompt 缓存(LANGFUSE_CACHE_PROMPT_ENABLED 默认值为 true)。用户通过 /api/public/v2/prompts/{name}?version=N 或 ?label=N 获取 Prompt 时触发。该问题会影响生产环境中按版本或标签固定 Prompt 的使用。
报错原文
The server side prompt cache builds its Redis key without distinguishing a version lookup from a label lookup, so a version number and a numeric label with the same text share one cache entry and return each other's content.
In packages/shared/src/server/services/PromptService/index.ts, getCacheKey returns:
`${prefix}:${params.version ?? params.label}`
where prefix is `prompt:{projectId}:{epoch}:{promptName}`. version is a number and label is a user provided string. Labels are allowed to be purely numeric.
So a request for version 3 and a request for label "3" produce the identical key `prompt:{projectId}:{epoch}:{name}:3` and share one cached value. Whichever request populates the key first wins, and the other request gets a cache hit with the wrong prompt version.
原因分析
根本原因是 getCacheKey 函数(位于 packages/shared/src/server/services/PromptService/index.ts,约第 198 行)将 params.version ?? params.label 直接拼接为缓存键后缀,而没有对查找类型(version 或 label)进行区分。由于 Langfuse 允许标签值为纯数字(正则 /^[a-z0-9_\-\.]+$/),当版本号 N 与标签值 “N” 相同时,两者共享同一个 Redis 键,导致首次请求确定缓存内容后,后续请求返回错误的 Prompt 版本。
环境排查
- 确认是否启用了 Prompt 缓存:检查环境变量
LANGFUSE_CACHE_PROMPT_ENABLED,未设置或设为true即受影响。 - 确认是否使用 Redis 作为缓存后端(v3 部署标准配置)。
- 确认项目中是否存在同一 Prompt 名称下,某个版本号 N 同时存在于其他版本的自定义标签中(例如:版本 3 存在,同时其他版本有标签 “3”)。
- 检查
packages/shared/src/server/services/PromptService/index.ts中getCacheKey函数的实现。
解决步骤
- 修改缓存键生成逻辑:在
getCacheKey函数中,将 version 和 label 分别添加类型前缀,例如version:${params.version}和label:${params.label},以避免命名空间碰撞。这是官方确认的修复方案,可优先尝试。 - 无需迁移旧缓存:由于 PR #12363 引入了 epoch 失效机制,旧的缓存键会在 TTL 到期后自然过期,不需要手动刷新或迁移。
- 添加回归测试:建议新增测试用例,验证
version=N和label="N"返回不同的缓存键和不同的 Prompt 内容。
验证方法
在修复后,重复复现步骤:创建同名 Prompt,版本 3 内容为 A,版本 7 添加标签 “3” 且内容为 B。先请求 ?version=3 应返回 A,再请求 ?label=3 应返回 B(而不是缓存中的 A)。对称验证先请求标签,再请求版本,两者返回均正确即表示修复生效。



