解决一个神出鬼没的 Bug

一句话说明问题:

升级 org-zettel-ref-mode 的数据库格式, 导致加载数据库的函数无法读取正确的数据库. 结果在内存里, 引发了一个错误链. 然而, 报错消息只显示 “let*: End of file during parsing”, 但无具体详细的报告 – 直接误导了问题的解决方向.

辛苦的过程:

由于报错消息直接说明, 代码的括号匹配出问题. 所以一开始是检查代码的括号匹配, 到 Emacs 群里吐槽了一下自己头都要秃了, 顺便把代码发过去请教一下, 高手也回复说是括号匹配. 但括号匹配怎么都查不出问题来, 所以换一个方式, 怀疑 let* 导致了错误的加载. 于是把所有 let* 里引用的变量前面全部都恢复用 let. 报错仍然存在. 整个过程相当令人疲惫.

灵光一闪:

由于之前总结, 如果解决一个 Bug 的方式,重复了 3 次, Bug 依然存在, 那么就要赶紧想想是否别的地方出问题. 不要吊死在一个 Bug 上. 于是, 我想一想, 不妨直接运行导致报错消息的函数试一下看看, 撞大运了, 这一次 Emacs 提供了具体而详细的错误报告. 我终于可以分析问题哪里, 然后顺利解决.

这一个 Bug 出现得很诡异, 它非常隐蔽以后都得注意, 不要导致同样的问题出现.

我让 Claude 帮我解读如下, 问题是数据库格式在我升级了之后, 没有接着升级数据库装载函数, 导致了如下问题, 由于问题存在于内存里, Emacs 未能捕捉到真正的问题:

通过查看错误堆栈和原始函数,我发现问题:

  1. 错误堆栈顺序:
(let ((data (read (current-buffer)))) ; read 失败,返回 nil
(let ((temp-db (make-hash-table :test 'equal)))
(dolist (entry-data (plist-get data :entries)) ; data 是 nil, plist-get 返回 nil
  1. 关键问题:
  • read 函数失败后返回 nil
  • 但代码继续执行,试图从 nil 中获取 :entries
  • 而后续函数继续使用这些无效值

最关键的是,这个错误会传递到调用链:

org-zettel-ref-db-load()
-> (let* (...) ...)  ; org-zettel-ref-init 中的嵌套 let* 

导致 org-zettel-ref-init 中的 let* 无法正常执行,从而引发了 “let*: End of file during parsing” 错误。

2 个赞