DEEP 193 · 第十六辑 · 小白基础课 · LEARN NOTES

收进学习手册 · 2026-09-11 · duck.whatled.com

解读 193 · 读报错的艺术

解读对象:概念课 · 以 microduck-replica/构建日志.md「踩过的坑」一节为教材(全部真实踩坑记录)
需要的前置:解读 192(打印、断言与日志);第 186 篇(Python 环境:uv 入门)的 uv run 命令格式。

报错不是失败,是程序在用它的语言跟你说话。读得懂,每个报错都是一次免费的教学。构建日志的「踩过的坑」一节自评为"整份日志最有价值的部分——别人可以不重复踩",每条都按「现象 → 原因 → 解决」写。我们就精读其中三条,把"读报错"的方法论拆出来。

案例一:IndexError,报错在最上层,病根在最底层

现象:在一台没有 GPU 的 Mac 上跑 uv run train,程序直接崩,抛 IndexError: list index out of range(出自 mjlab 的 utils/gpu.py,函数 select_gpus)。

读法:IndexError 是"按编号取列表,编号超长了"。但先别急着在自己代码里找列表——先看崩在哪个文件的哪个函数:select_gpus,选 GPU 的。再问一句"为什么它会去选 GPU":训练器默认 gpu_ids=[0],即"用第 0 块 GPU";这台机器没有 CUDA,GPU 列表是空的,按 0 号取值就越界了。

解决:显式声明"我没有 GPU"——环境变量 CUDA_VISIBLE_DEVICES=""(这是 mjlab 官方约定的显式 CPU 模式,gpu.py 的文档字符串写明)。一条命令不改,环境先答话。

案例二:报错只说"出错了",答案在默认值里

现象:同一天的第二个坑——刚修好 GPU 报错,程序又卡在 wandb 上:默认的实验记录工具是 wandb,而本机没配它的 API key,直接报错。

读法:这类报错的根源往往不是你写错了什么,而是工具链替你做了一次你没同意的选择("默认用 wandb")。读法是反问:这个默认是谁定的?能不能换?

解决:换成不联网的 TensorBoard——--agent.logger tensorboard(与官方冒烟测试参数一致)。注意这条解决和案例一连起来才完整:报错经常是一个接一个出现的,修好第一个才会看见第二个。别指望一次跑通,也别因为"又报错了"而灰心——每次崩的位置都前进了。

案例三:程序不报错,但结果是错的——最贵的一类

现象:给评估脚本写输出路径时用了 ../rl-series/... 这样的相对路径,从 upstream 目录一运行,文件写到了错误的位置——日志原话:"本会话第二次栽在相对路径上"。

读法:相对路径的"参照物"是运行程序时所在的目录,不是脚本文件所在的目录。同一个脚本,换个目录启动,.. 就指去了别处。它难缠在不崩溃:文件照样生成,只是生成在错的地方,你事后找数据时才会发现不对。

解决:教训已固化成纪律——脚本内一律基于 __file__(脚本自身在磁盘上的位置)定位仓库相对路径,让"从哪运行"变得无关紧要。

你带走的收获

延伸

什么鸭 WHATDUCK · 复刻学习笔记 ← 全部手册 · 首页