一个配置文件,在 Linux 上跑得好好的,搬到 Windows 上就报
Invalid config。
报错信息只说"Invalid input",却不告诉你是哪一行。
我花了整整一晚,和 OpenClaw 的 schema 校验搏斗。
事故现场
把 AI 数字员工队伍部署到用户的 Windows 电脑上。一切就绪,点下"启动 OpenClaw":
🦞 OpenClaw 2026.7.1-2 (0790d9f) [gateway] loading configuration… [gateway] resolving authentication… Gateway failed to start: Invalid config: models.providers.deepseek.models.0: Invalid input agents.list.0: Invalid input agents.list.1: Invalid input第一反应:配置文件写错了?打开 openclaw.json——格式规整、缩进正确、字段齐全。
奇怪的是:同一份配置(除了路径),在 Linux 开发机上跑得好好的——小虾、小牛两个智能体正常工作。
同一个配置,Linux 能跑,Windows 报错。这不是"写错了",这是"我看不懂它要什么"。
第一层:Missing config——配置根本没被读到
先解决前置问题。第一次启动时报的是:
Missing config. Run `openclaw setup` or set gateway.mode=local配置里明明有"mode": "local"。为什么说 Missing?
看 openclaw 的源码(openclaw.mjs):
consthomeDir=resolveLauncherHomeDir();return[path.join(homeDir,".openclaw","openclaw.json"),path.join(homeDir,".clawdbot","openclaw.json"),];resolveLauncherHomeDir()读环境变量OPENCLAW_HOME。我们设了:
OPENCLAW_HOME = E:\ai-team-collab\openclaw于是 openclaw 找的是:
E:\ai-team-collab\openclaw\.openclaw\openclaw.json注意这个.openclaw子目录——我们的配置放在E:\ai-team-collab\openclaw\openclaw.json(没有子目录)。配置存在,但放错了位置,等于没有。
修复:把配置双写一份到.openclaw子目录。
根因一:配置文件的位置语义——“存在"不等于"被读到”。
第二层:models.0.name: Invalid input——缺字段
位置对了,下一个错误冒出来:
models.providers.deepseek.models.0.name: Invalid input错误信息进步了——从"整个对象无效"变成"具体字段无效":models.0.name。
看我们的配置:
"models":[{"id":"deepseek-v4-flash","input":["text"],"output":["text"]}]只有id和input/output,没有name。openclaw 的 schema 要求模型必须有name。
补上:
{"id":"deepseek-v4-flash","name":"deepseek-v4-flash","input":["text"],"output":["text"]}根因二:schema 要求
name必填——缺一个字段,整个对象无效。
第三层:models.0: Invalid input——多出来的字段
name补上了,错误变回了"整个对象无效":
models.providers.deepseek.models.0: Invalid input这次没有具体字段名了——不是"缺",是"多"。
对比 Linux 上能跑的配置:
// Linux 能跑{"id":"mimo-v2.5","input":["text","image"],"name":"mimo-v2.5"}// 我们报错的{"id":"deepseek-v4-flash","name":"deepseek-v4-flash","input":["text"],"output":["text"]}差异一目了然:Linux 的 models 没有output字段。
OpenClaw 的 schema 是严格校验——未知字段 = 无效。output不是它认识的字段,整个对象就被判死刑。
去掉output,input 改成["text", "image"]:
{"id":"deepseek-v4-flash","name":"deepseek-v4-flash","input":["text","image"]}根因三:strict schema——多一个不认识的字段,和少一个必填字段,结局一样:Invalid。
第四层:agents.list.0: Invalid input——照抄"能跑的配置"
models 修好了,轮到 agents:
agents.list.0: Invalid input agents.list.1: Invalid input继续对照 Linux 能跑的配置:
// Linux 能跑{"id":"main","model":{"primary":"deepseek/deepseek-v4-flash"},"heartbeat":{"every":"12h"},"identity":{"name":"小虾"}}// 我们报错的{"id":"main","workspace":"E:\\...","model":{"primary":"deepseek/deepseek-v4-flash","fallbacks":[]},"heartbeat":{"every":"12h"},"identity":{"name":"小虾"},"name":"小虾",// ← 多余的"imageModel":"小米/mimo-v2.5"// ← 多余的}三个多余字段:
name——身份已经在identity.name里了,schema 不认 agent 级的nameimageModel——视觉模型配置,schema 不认这个位置的字符串fallbacks: []——空数组,Linux 的 agent 根本没写 fallbacks
逐个删掉,agent 只留:
{"id":"main","model":{"primary":"deepseek/deepseek-v4-flash"},"heartbeat":{"every":"12h"},"identity":{"name":"小虾"}}启动——成功。
复盘:为什么"能跑的配置"是最好的文档
这一晚的排查,本质是一次一次对照"能跑的配置"做 diff:
| 报错 | 我们写的 | 能跑的配置 | 修复 |
|---|---|---|---|
| models.0.name | 缺 name | 有 name | 补 name |
| models.0 | 有 output | 无 output | 删 output |
| agents.list.0 | 有 name/imageModel/fallbacks | 只有 id/model/heartbeat/identity | 删多余字段 |
错误信息只告诉你Invalid,不告诉你Valid 长什么样。
而"能跑的配置"——就在开发机上跑着的那个——就是答案本身。
三个教训
1. strict schema 是双刃剑。
校验严格,配置错误能早发现;但也意味着:多写一个字段 = 少写一个字段 = 一样报错。生成配置的代码,必须照着官方/已验证的模板拼,不能想当然"字段越多越保险"。
2. 平台差异不是玄学,是版本差异。
Linux 的 openclaw 2026.7.1 接受宽松格式,Windows 的 2026.7.1-2 schema 更严格。同一个大版本号,小版本之间 schema 可能完全不同。"在我机器上能跑"不是答案,"在我这个版本上能跑"才是。
3. 配置生成器要"保守"。
我们给 save_config 写配置生成逻辑时,走了两次弯路——第一次多写了 name 和 imageModel,第二次多写了 output 和 fallbacks。生成器宁可少写可选字段,也不要写 schema 不认识的字段。少写的字段 openclaw 用默认值;多写的字段直接 Invalid。
报错信息只告诉你什么是 Invalid,
能跑的配置才告诉你什么是 Valid。
排查配置问题最快的路,
是找到一份正在运行的配置,逐字段 diff。
(真实事故记录:OpenClaw 2026.7.1-2 Windows 部署,配置校验排查。从 Missing config 到 Invalid config,四层错误,四个根因——配置位置、缺字段、多字段、格式版本差异。)