DSH read_image 图片工具报 cannot get property "fs" without inject?DeepSeek Harness 排查
DSH 调用原生 read_image 工具立即报 Error: cannot get property "fs" without inject,而 read / write / edit 全部正常——根因是 DSH 原生包 @deepseek-ai/dsh-tool-fs 的一行 scope 传错:apply() 把 read_image 注册在 attachments 门控的子上下文里,却把收窄后的 imageCtx 传给了执行体,执行体按属性访问 ctx.fs 触发 cordis 守卫。 这是 DSH 本体的 bug,装任何插件都不改变它;临时方案是走外部视觉 CLI,正式修复需等官方合入后升级。
read_image 报 cannot get property "fs" without inject 长什么样
报错固定为 Error: cannot get property "fs" without inject,只在调用 read_image 时出现,且与图片文件本身无关。 报告者实跑复现(讨论原文):
- 环境:dsh
0.1.0-rc.6、Windows、web profile,freedompreset 复现,凡挂载@deepseek-ai/dsh-tool-fs的 preset 理论上都中招(缺陷在包本身); read/write/edit正常,唯独read_image失败——换任意图片文件都一样;- 这条链上每一步看起来都是健康的:插件挂载成功 → 工具注册成功、出现在模型可见的工具目录里 → 模型看得到它、也会调它 → 直到真正执行那一刻才炸;
- 代价是模型白白烧掉一整轮——它不知道这个工具坏了,只知道调用失败了。
根因:注册在收窄子上下文里的 read_image
@deepseek-ai/dsh-tool-fs/lib/index.js 的 apply() 在 attachments 门控子上下文里注册 read_image,并把那个收窄的上下文传给了执行体。 代码形状如下(来源):
const inject = ["tools", "fs", "systemPrompt"]; // 插件级声明
function apply(ctx, config) {
applyReadTool(ctx); // 正常:外层 ctx 声明了 fs
ctx.inject(["attachments"], (imageCtx) => {
applyReadImageTool(imageCtx); // ← imageCtx 只声明了 "attachments"
});
// ...
}
而 applyReadImageTool 的执行体按属性访问 ctx.fs.readBytes(...)(并经 ctx.tools.register(...) 注册)。Cordis 的守卫对未声明的服务属性访问直接抛错,于是执行死在 cannot get property "fs" without inject。函数自己的注释写明了预期契约——「执行使用其 fs 服务,加上可选的 attachments / llm 服务」——即调用点本应传外层 scope,实际却传了收窄后的访问器。执行体对可选服务其实已经用 ctx.get("attachments") / ctx.get("llm") 做了正确的运行时查找,只有 fs、tools 这类声明式属性访问受了影响。
为什么能通过所有挂载期检查
因为守卫在执行期才响。 这个 bug 的形状值得单独说:插件挂载成功、工具注册成功、模型可见、模型调用——全部正常,直到执行那一下。社区里同一家族还有:
- #1782:插件挂载成功(
fiberPhase: "active"、构造函数无报错),但它注册的命令和工具对 agent 完全不可见; - #2917:
dsh plugin addexit 0、--dump-config显示一切正常,然后 harness 完全起不来——因为--dump-config只组合、不启动。
共同点:「挂载/组合成功」这个信号,不保证注册的东西真的可用。 而你这条是这一族里症状最良性的一个——cordis 的报错明确点名了缺哪个服务(cannot get property "fs" without inject),在社区里算难得的;同族其它成员大多是静默的。这也是支持「注册期就该校验 scope」这类改进的正面例子。
怎么排查与临时绕行
先确认是不是这个 bug:只有 read_image 崩、兄弟工具全正常,且与图片无关——那就是它。 按步骤确认:
- 核对报错形态:调用
read_image得到cannot get property "fs" without inject,同一会话里read/write/edit均正常; - 确认无关插件:这是
@deepseek-ai/dsh-tool-fs包内的缺陷,装什么插件都不改变它——不要先怀疑刚装的插件; - 等待官方修复:社区已定位到调用点(
src/index.ts把imageCtx传进applyReadImageTool,而执行体访问ctx.tools/ctx.fs),升级含修复的 dsh 版本后验证read_image直接向模型返回图片; - 临时绕行:把图片读取路由到外部视觉 CLI(如 modlens),绕开 DSH 工具管线——这也是这个 bug 存在一段时间没人报的原因:遇到的人第一反应是绕过去,绕过去就不回来报了。
修复方向:调用点一行改动(面向维护者)
推荐修法是把外层 ctx 传进 applyReadImageTool,而不是扩宽内层 inject。 两条都能过守卫,但语义不同:
- 传外层
ctx:声明的是「工具执行依赖fs/tools,attachments只是注册时机门」——与函数注释的契约一致,也与执行体已有的ctx.get("attachments")/ctx.get("llm")运行时查找写法一致:
ctx.inject(["attachments"], () => {
applyReadImageTool(ctx);
});
-
扩宽内层 inject 到
["attachments", "fs", "tools"]:能用,但表达的意思错了——fs并不需要等attachments就绪;将来有人调整 attachments 的可用条件,这个写法会连带影响fs的解析时机。 -
回归测试要求:现有生命周期测试只证明
read_image随 attachment fiber 挂载/销毁出现和消失,schema 可见性没有触发守卫属性访问。回归用例应调用完全组合后的ToolFs插件拿到的工具并要求返回成功图片结果,并保留三组配对断言:attachment 挂载 → 工具可见且可执行;attachment 销毁 → 只有read_image撤回;重新挂载 → 再次可见且可执行。
注意事项
- 别把问题归到刚装的插件上——这是 DSH 原生包
dsh-tool-fs的缺陷,与插件无关。 - 模型会在
read_image上白烧一轮,可提示模型改用其它方式读图(如让用户描述),或直接走外部视觉 CLI。 - 排查图片问题时,#4615(
dsh-llm-pi-ai把非 png/jpeg/gif 图片原样发给严格 OpenAI 兼容后端)与本文同属「图片路径」一带,两条一起看能省时间。 - 同类插件报错还汇总在《DeepSeek Harness 插件报错合集:DSH plugin 不加载、Web UI 异常与会话缓存修复》里。
常见问题
@deepseek-ai/dsh-tool-fs 的 apply() 把 read_image 注册在 ctx.inject(["attachments"], …) 收窄过的子上下文里,却把这个收窄后的 imageCtx 传给了执行体;执行体按属性访问 ctx.fs / ctx.tools,cordis 的守卫在执行期抛错(来源)。
read/write/edit 在 apply() 里直接由外层 ctx 注册(声明了 fs),只有 read_image 被包进 attachments 门控子上下文并拿到收窄 scope——执行体用它访问 ctx.fs 时触雷,与图片文件本身无关。
cordis 的服务属性守卫只在执行期响应:插件挂载成功、工具注册进模型可见目录、模型看到并调用,直到真正执行才炸。挂载/组合成功这个信号不保证注册的东西真的可用,同类问题还有 #1782、#2917。
这是 DSH 原生包的一行 scope 传错,装任何插件都不改变它,只能等官方修复后升级 dsh。临时方案是绕开 DSH 工具管线,把图片读取路由到外部视觉 CLI(如 modlens)。
属于 DSH 本体(@deepseek-ai/dsh-tool-fs 在 DSH 内)。社区已给出调用点一行修复(把外层 ctx 传给 applyReadImageTool),报告者已在 Windows 上验证通过,属良性报错——cordis 明确点名了缺失的服务。
来源
- deepseek-harness Discussion #4612:原生 read_image 工具报 cannot get property 'fs' without inject(全 preset 复现)· deepseek-ai(GitHub Discussions)
- deepseek-harness Discussion #1782:插件挂载成功但命令与工具对 agent 不可见· deepseek-ai(GitHub Discussions)
- deepseek-harness Discussion #2917:dsh plugin add exit 0、--dump-config 正常但 harness 起不来· deepseek-ai(GitHub Discussions)