今天本来只是想顺手处理一下 OpenClaw 里 memory 相关的报错,结果又被一个看着不大、但特别容易把人带偏的小问题绊住了。
[tools] read failed: ENOENT: no such file or directory, access '/path/to/workspace/MEMORY.md' [tools] read failed: ENOENT: no such file or directory, access '/path/to/workspace/memory/2026-04-08.md'
如果只看字面意思,那就是文件不存在。
但麻烦就在于,这种报错放在 agent workspace 这种环境里,特别容易让人多想。看到 MEMORY.md 报错,第一反应往往不会只是“哦,没这个文件”,而是会开始怀疑:是不是 memory 系统出问题了,是不是路径配置错了,是不是某个 skill 把原来的记忆文件替掉了,甚至会不会连聊天记录都出了问题。
这类问题最烦的地方,不是它特别难,而是它特别像那种“值得立刻警觉的大问题”,结果查到最后发现,真正麻烦的是几个概念混在一起。
🧩 这次真正踩到的坑
1. MEMORY.md 和 daily memory 本来就不一定存在
这个其实是最容易被误判的一层。
像 MEMORY.md、memory/YYYY-MM-DD.md 这类文件,看上去很像某种“系统理应自动准备好的基础文件”,但实际不是这么回事。它们更像是给 agent 持续积累记忆用的载体,而不是每个 workspace 初始化完就必然存在的东西。
- 这个 workspace 可能还没写长期记忆
- 当天可能还没有 daily memory
- 或者对应文件根本还没被创建过
所以一个文件不存在,本身并不稀奇。问题只是,启动流程如果写成默认去读,那它就会老老实实去读;读不到,就给你甩一个 ENOENT。
于是日志看上去像出错了,但很多时候它只是去读了一个本来就可能不存在的文件。
2. self-improving 那套文件,不等于把原来的 memory 替掉了
我这次排的时候,中途也很自然地怀疑过另一件事:会不会是 self-improving 之类的 skill 加进来之后,原来那套 MEMORY.md 被迁走了,或者被别的学习文件顶掉了。
.learnings/LEARNINGS.mdERRORS.mdFEATURE_REQUESTS.md
这些名字看久了,确实很容易让人脑补出“旧 memory 机制是不是已经被新版 learning 逻辑替换了”这种结论。
但至少这次排下来的结果不是这样。更接近实际的情况是:
MEMORY.md还是长期记忆那一层memory/YYYY-MM-DD*.md还是 daily / session 侧的记忆文件.learnings/这类东西,是另一套学习、纠错、经验沉淀用的文件
它们会并存,但不是同一个东西。
3. 完整聊天记录也不是这些 Markdown 文件
这一点也挺重要,不然排着排着很容易自己把自己吓到。
- 完整聊天记录
MEMORY.mdmemory/YYYY-MM-DD.md- 某个 agent 顺手写出来的 Markdown 文档
很多时候大家会把这些东西默认当成同一类,但它们其实不是一回事。
从 OpenClaw 这套东西的语义来看,完整会话记录更接近 gateway / session transcript 那一层,通常是 JSONL 存储;workspace 里的这些 Markdown 文件,更像是 agent 拿来整理、摘要、续上下文的“记忆文件”。
所以,
MEMORY.md不存在,不等于你的完整聊天记录没了。
😮💨 这事为什么这么磨人
说穿了,是因为它属于那种非常典型的“看起来像大问题的小问题”。
报错是真的,文件也确实不存在,但“文件不存在”本身未必说明系统坏了。真正让人头疼的,是这几层东西太容易在脑子里混在一起:
- 哪些是长期记忆
- 哪些是 daily memory
- 哪些是 self-improving 的学习记录
- 哪些又是完整 transcript
- 以及最后,到底是 agent 的启动规则在读这些东西,还是底层实现本身有问题
这些概念只要没先拆开,后面排查就很容易一路歪掉。
而且更烦的是,最后真正落地的处理并不复杂。这次我最后做的事情,其实只是把几个 workspace 里的启动语义改得更合理一点:
- 有这个记忆文件,就读
- 没有,就静默跳过
就这么简单。
你回头一看,会有种很微妙的烦躁感:前面花了不少时间把概念捋顺,最后真正改的东西却没有多大。但也正因为这样,这事才值得记一下。
🔧 这次我更愿意把它理解成“降噪”
我不太想把这种事情写成那种很夸张的“彻底修复某某核心问题”。这次实际更像是在做一件很朴素的事:把一些本来就允许缺省的情况,从“像事故一样的日志”里剥出来。
至少从使用体验上说,这一步挺值。因为很多时候真正烧时间、烧注意力、甚至烧模型调用成本的,不是那种明显的大 bug,而是这种会反复让你停下来确认“它到底算不算 bug”的东西。
📌 最后留个比较实用的想法
MEMORY.md和 daily memory 是记忆文件,但不是每次都必须预先存在- self-improving / learning 那些文件是另一层,不要看到名字像就以为它们在接管旧逻辑
- transcript 和 Markdown memory 不是一个东西
- 启动规则写成“存在才读,不存在跳过”,会比无脑读取省很多事
不是什么惊天动地的结论,但至少很实用。
这次最大的感受也差不多就是:很多时候最烦的不是系统真坏了,而是它没完全坏,但又特别像坏了。而这种情况,往往比真正的大故障更磨人。
