Repository navigation
[BUG] DSH 0.2.1-rc.3 + dsh-ops 0.4.1:PTC 回合第二次组装抛 Cannot read properties of undefined (reading 'length'),整轮中断 #412
Description
环境
- DSH
0.2.1-rc.3(macOS arm64,profiledsh-ops-mac) - dsh-ops
0.4.1 - 呈现:PTC(
run_code是唯一可直接调用的工具,tools:ptc-only段非空) - 复现宿主:同一 checkout 的
master(dsh-v0.2.1-rc.3),Node 24.17.0
现象
任何 PTC 回合,第一步的 step/end 之后立刻 turn/end,整轮中断,客户端只显示一句:
Cannot read properties of undefined (reading 'length')
会话文件里的形状很固定 —— 有 step/start{turn:1,step:1}、tool/call、tool/ptc-dispatch、tool/result、step/end{turn:1,step:1},然后直接 turn/end{turn:1,reason:{kind:"error",code:"UNKNOWN",message:"Cannot read properties of undefined (reading 'length')"}};没有 step/start{turn:1,step:2},也没有第二次 request/header。也就是说:第一次模型调用成功、run_code 程序也跑完了,是准备第二步时炸的。
根因
DSH 0.2.1-rc.3 的 9d0c97b9d2 feat(tools): admit conditional tools per step(git describe = dsh-v0.2.1-rc.2-677-g9d0c97b9d2,提交说明里写明 “system-prompt/assemble now transforms PromptPresentation without a tool array”)改了瀑布契约:
0.2.0-rc.2:system-prompt/assemble收到的assembly是完整PromptAssembly,含tools0.2.1-rc.3:瀑布只变换PromptPresentation(sections/contexts/variables),tools由SystemPrompt.assemble()在瀑布之后追加(packages/core/system-prompt/src/index.ts:return { ...transformed, tools })
src/ptc/index.ts 的 isPtc() 读的是瀑布返回值:
const isPtc = (assembly: PromptAssembly): boolean => assembly.tools.length === 1 && assembly.tools[0]?.name === 'run_code'
&& assembly.sections.some(section => section.name === 'tools:ptc-only' && section.text.trim() !== '')在 system-prompt/assemble 监听里 const result = await next(); ... isPtc(result),result.tools 在 0.2.1 上恒为 undefined → 抛 TypeError。
为什么只在“第二次组装”炸
同一监听有前置门控 heldPtc = Boolean(entry?.ptc && entry.owned.size)。第一步的组装发生在 agent/pre-step 把 PTC 工具面发出去之前(heldPtc === false,短路返回,不碰 isPtc),所以第一步永远正常;第二步起 heldPtc === true,必然踩空。这解释了“错误总是在第一次工具调用之后出现”的观感。
栈
TypeError: Cannot read properties of undefined (reading 'length')
at isPtc (dsh-ops/lib/ptc/index.js:21:44)
at Object.<anonymous> (dsh-ops/lib/ptc/index.js:259:39) // system-prompt/assemble 监听
at async Proxy.assemble (@deepseek-ai/dsh-system-prompt/lib/index.js:399:23)
at async ReactLoopAgent.preStep (@deepseek-ai/dsh-agent-loop/lib/index.js:679:21)
at async ReactLoopAgent.turn (@deepseek-ai/dsh-agent-loop/lib/index.js:732)
preStep 把异常直接抛给 turn 循环,所以整轮结束,且错误码落成 UNKNOWN。
同一契约变更还有第二处
src/official-tool-hiding.ts 的组装校验:
if (!Array.isArray(result?.tools)) throw block(context.scope, 'assembled tool schemas unavailable')在 0.2.1 上同样恒真 → 只要 profile 启用了 dsh-ops-hide-official 且 replaceTools 非空,每个作用域的组装都会被 READINESS_BLOCKED ... assembled tool schemas unavailable 拦下。本次 profile 没启用这一行,所以先暴露的是 PTC 那条;两处是同一个假设。
(src/discovery/index.ts 的同名判断只看 tools:ptc-only 段,未受影响。)
最小复现
- DSH
0.2.1-rc.3+ dsh-ops0.4.1,profile 里启用dsh-ops-ptc(PTC 呈现) - 任意新会话,发一句会触发工具调用的话,例如让它读一个文件
- 模型调用一次
run_code(程序内调tools.ops_bash/tools.ops_inspect_local_file均可) - 工具返回后整轮报
Cannot read properties of undefined (reading 'length')
不依赖具体工具内容:只要这一步是 PTC 呈现下的第二次组装就会复现。用真实的 0.2.1-rc.3 AgentLoop + 0.4.1 的 lib/ 组一个脚本化 provider 可以稳定复现(第一步 run_code、第二步无工具调用),修复前固定报同一个 TypeError,修复后两步都正常。
期望行为
- PTC 回合应能走到第二步;
system-prompt/assemble的监听不得假设载荷里有tools 0.2.0的语义要保留:有tools时仍按tools.length === 1 && tools[0].name === 'run_code'+ 段落双重判定hide-official在载荷没有工具数组时,应改用注册表视图(visibleToolNames())完成同一项校验,而不是无条件 block
影响面
- 0.2.1-rc.3 上只要走 PTC 呈现,dsh-ops 的 PTC 能力等于完全不可用(不是某条命令失败,是回合必断)
package.json声明peerDependencies["@deepseek-ai/dsh"] = "^0.2.0-rc.2",按 semver0.2.1-rc.3属于声明支持范围,所以这不是“不支持的组合”,而是兼容性缺陷
建议修复
把 PTC-only 判定抽成不依赖 tools 存在性的谓词(无数组时用提供方拥有的 tools:ptc-only 段判定,与 isNativePresentation、discovery 同一口径),hide-official 的校验回退到注册表视图。本地已有补丁 + 三条回归测试(真实 loop 双步、hiding 组装、谓词单测),需要的话我可以直接提 PR。
关联
- [BUG] 0.4.1 在 DSH 0.2.1-rc.2 (Windows) 上不发布 ops_discover_tools,且全程无日志 #346 是同一宿主版本区间(0.2.1-rc.x)的另一处 0.4.1 兼容问题
Activity
WeiLinCool commentedon Oct 26, 2026
和 #387 重复了。
#387 已在 2026-10-25 由
211dc3e fix(session): defer Bash publication and accept text-only prompt hooks在main上修好(hasPtcInstruction+ text-only hooks:瀑布载荷没有tools时只按tools:ptc-only段判定)。我用真实 DSH0.2.1-rc.3的 AgentLoop + 本仓库lib/复跑确认:main@dfafddc上同一个会话两步都正常,0.4.1的构建则在第二次组装上抛Cannot read properties of undefined (reading 'length')。但
0.4.1是当前发布版(npm 最新也是 0.4.1),还不含211dc3e,所以0.4.1 + DSH 0.2.1-rc.3仍然必现,需要一次 0.4.2 才能让用户拿到修复。此外同一次契约变更还有一处没跟上:hide-official 的组装校验(
src/official-tool-hiding.ts:176)在main上仍无条件要求载荷带tools,一旦启用该行(replaceTools非空)每个作用域都会被READINESS_BLOCKED ... assembled tool schemas unavailable拦下。已另开 #413,修复见 PR #414。本 issue 按重复关闭;PTC 侧请跟 #387 与 0.4.2 的发布进度。