Margrop
Articles390
Tags970
Categories7

Categories

/health 200 /v1/models 0.025s 0步 0步主动 0步元递归 0步本身 12类 18789 18天idle 18天静默 192.168.x.x 1password 2.3s 2013 21天 22类一键汇总 3层定位法 3行修复 3行修改 4 节点共享 400 401 4个Gateway 4个Gateway全军覆没 4天滞后 4步主动 4步定位 4源 4源交叉 503 5步定位法 5步排查 5步验证 6.2.0 6.24 release 6.28 发现 60秒延迟 60秒超时 6个host 6个节点 6节点 AC ACP AI AI Coding Assistant AI编程助手 AI辅助 AI辅助编程 ALLHEALTHY AP API API 改动 ActiveState Agent couldn't generate Alertmanager AppDaemon Aqara BaiduPCS CC-Switch CI/CD CLI Tools CLI工具 CONFIG Caddy Chrome缺失 Claude Code Cloudflare Codex Cookie 认证 Cron D1 DB探针 DB静止 DIY-123模型 DIY-MINI DIY平台 Date Diagrams.net Diary Docker Docker Compose EADDRINUSE EasyTier NAT穿透 Efficiency Tools Electerm English FTS5 Gateway Gemini CLI GitHub Actions HA HADashboard HTTP 200 Hermes Hexo HomeAssistant INVALID_PARAMS IP IPv4 Invalid model Java LVM‑Thin Library/Logs Linux MacMini MacOS Macmini Macmini log路径 Markdown MiniMax MiniMax-M2-7-fallback MiniMax-M3 Multi-Agent MySQL NAS NRestarts Nginx Node-RED Node.js OOM OpenAI OpenClaw OpenClaw gateway OpenCode OpenResty OpenWrt P1P3 PPID PPID=1 PPID=796 PPPoE PVE PVE245 Portainer PostgreSQL ProcessOn Prometheus Proxmox VE RPC Restart=always Restart=always循环 SOCKS5 SPOF SQLite SSL Session Shell Subagent TTS TimeMachine Type=notify UML Uptime Kuma VM VM151 VM152 WeCom缺失 VM153 VM154 VPN VPS VPS4 VPS4 overlay TCP不可达 WeCom Web WebSocket Windows Workers activate ad adb adblock agent alerting alias 取消 aligenie aliyun alpine annotation aop argv authy auto recovery auto-restart autofs backup baidupan baidupcs baidupcs-sync-progress baidupcs静默 bash bash subprocess bitwarden boot breaking change brew browser by-design caddy2 capture_output cdn centos cert certbot charles chat chat completion chat completions chrome classloader client clone closures cloudflare cmd command commit connected container cron crontab cron任务 cron设计 cross-verification ctyun curl custom/DIY-123 daemon-reload dashboard ddsm demo dependency deploy deprecation developer devtools dll dns docker domain download draw drawio dsm dual supervision dump duplicate service unit dylib edge exception existing gateway is healthy exit 78 exit code exit78 export fail2ban failover fallback fallback chain fallback失效 false negative false positive feign feishu告警 firewall-cmd flow frp frpc frps fuckgfw function fuser gateway gateway.log gcc gfw git gitea github golang google_gemma-4 gperftools grep gridea grub gvt-g hacs havcs health check health-check-all heap hello hexo hibernate hidden bomb hidpi hoisting homeassistant hosts html htmlparser https iKuai idea idle-detection idle_hours image img img2kvm immortalwrt import index install intel investigation io ios ip iptables iptv ipv6 iso java javascript jetbrains jieba jni jnilib journald journald日志漂移 jpa js json jsonb jupter jupyterlab jvm k8s kernel key kid kill orphan kms kodi koolproxy koolproxyr kvm lan lastpass launchctl learning lede letsencrypt linux live log path log rotate loopback-proxy low-code lsof lsof -p lvm lxc m3u8 mac macOS macOS app macos manual mariadb markdown maven md5 meta-acceptance meta-pattern meta-probe microcode minimax mirror misjudgment model alias model id model live test model provider modem modules monitor mount mstsc multisource mysql n2n n5105 nas netstat network new-api newapi nfs node node-red nodejs nohup notepad++ npm nssm ntp one-api oop openclaw openclaw/ openfeign openssl orphan process orphan进程 os otp ovz p14 packet capture pat pdf pem perf ping ping通但chat不通 pip plugin png port bind race port=18789 powerbutton print pro probe process check process detection provider/model proxy ps ps -axo args ps -eo args ps+grep pve pvekclean python python subprocess qcow2 qemu qemu-guest-agent qmshutdown rar reboot reconnect循环 reflog release notes remote remote desktop renew repo resize retina root route router rule rules running runtime safari sata schema schema列名 scipy-notebook scoping scp self-blind self-leak self-reference server server is busy service不可信 shared config single point of failure single source single-instance slmgr so socket-proxyd socks source spk split边界 spring springboot springfox sqlite3 CLI ss ss -tlnp ssh ssl stale stash stderr/stdout stderr被吞 stdout/stderr string subprocess supernode supervisor svg svn swagger sync synology system-level daemon system-level vs user-level system-level与user-level抢端口 systemctl systemctl --user systemctl --user disable systemctl daemon-reload systemctl disable systemctl is-active systemctl restart systemctl show systemd systemd --user systemd duplicate service systemd exit 78 systemd restart loop systemd service unit systemd unit systemd unit race systemd user instance systemd-socket systemd-user双重监管 systemd被覆盖 tap tap-windows tapwindows telecom template terminal tls tmux token token失效 totp transient 999 trigram tvbox txt typo ubuntu udisk ui undertow unicode61 unified logging uninstall unlocker upgrade upstream upstream alias upstream provider timeout uptimeMs url user-level daemon v1 v1 API v10探针 v11探针 v12探针 v13探针 v14 v15探针 v1探针 v2 API v2ray v6探针 v7探针 v8探针 vhd vim vlmcsd vm vmdk web websocket wechat windows with work day 14 work day 15 work day 17 work day 2 worker wow xiaoya xml yum zip 一行修改 一键idle告警脚本 一键告警脚本 一键解决方案 上海 上海晴 上游LLM容量 不动 不干预 不是我的锅 中国电信 中文搜索 主动0步 主动0步本身 主动不修 主动不追问 主动不追问本身 主动不追问本身也是清单之外 主动不通知 主动不通知本身 主动修 主动修system-level本身也是清单之外 主动修本身也是清单之外 主动反思 主动周一 主动意识到 主动意识到0步本身 主动意识到0步本身也是清单之外 主动排查 主动追问 主动通知 云电脑 交叉验证 交换机 人机协作 代理 伏笔 优化 但chat 30s+ 但是我的事 体检 保护逻辑本身也是清单之外 修systemd-user本身 修复方案 修挖坑闭环 修正本身 修正递归 值班 假阳 假阳性 假阴 健康检查 健康检查探针 元递归 光猫 克制 全HEALTHY 全员HEALTHY 全绿 全量同步 公网IP 共享配置 内存 内存优化 内网 内网IP 内网渗透 写作 分词 切换 列名误判 升级 协作 单位混淆 博客 又是周五 双重监管 反向代理 反向探针 反常健康 反常稳定 反常稳定本身 反应 vs 知识 反着来 启动 告警 告警优化 周一 周一焦虑 周三 周二 周二晚上 周二青岛后周三 周五 周五晚上 周六 周六晚上 周四 周四晚上 周报 周日 周日山崎 周日山崎后周一 周日晚上 周末 周末也是修坑日 周末也是清单之外 周末修坑 周末本身也是清单之外 周末突破 周末第二天 周末第五天 周末落地 周末落地本身 夏令时 多场景 多智能体 多源验证 多节点 多节点管理 大小写敏感 天猫精灵 天翼云 孤儿进程 安全 安装 定时任务 容器 容器网络 宿命雷 导入 小米 山崎 山崎之夜 工作感悟 工作日 工作日常 工作日第三天 工作日第五天 工作日第四天 已通知用户 常用软件 幂等 广告屏蔽 序列号 应用市场 异常 弃用 循环类 心态 心智成长 心理模型 心跳 心跳检查 性能优化 性能最快 感悟 打工 打工人 打工人的克制 打工人的反讽 打工人的无奈 打工人的自指 批量校验 技术 抓包 拼写错误 挖坑→修坑闭环 排查 排查思路 排查流程 探针 探针再升级 探针本身 探针版本 探针管理 探针自检 探针踩坑 接受 接受之后 接受修 接受修正 接受层 接受挖坑 接受本身 接受递归 描述文件 放下 故障 故障排查 效率 效率工具 数据 旁路由 旁路进程 无服务器 日志路径 日记 时区 显卡虚拟化 智能家居 智能音箱 服务器 服务管理 架构 梯子 模块 模型别名映射 模型探测 模型端点可达性 模型端点能ping通 模型调用 横线点 死循环 毫秒 流程 流程图 流程管理 浏览器 清单之后 清单之外 清单之外也包括接受本身 清单的元递归 清单设计 清单边界 清单进化 源码备份 漫游 激活 激活循环 火绒 焦虑 玄学 生活 用户主动 用户关机 电信 画图 监控 监控系统 直播源 直觉 磁盘 端口 端口 LISTEN 端口冲突 端口占用 端口扫描 第10天 第10类 第11天 第11类 第12天 第12类 第13天 第13类 第14天 第14类 第15类 第16天 第16类 第17个青岛 第17类 第18天 第18类 第19天 第19类 第20天 第20类 第21天 第21类 第22天 第22类 第23天 第23类 第24天 第25天 第25类 第26类 第27类 第28类 第29类 第30类 第31类 第32类 第33类 第4个山崎 第4次复发 第6天 第7天 第8天 第9天 第9类 管理 续期 网关 网络 网络风暴 群晖 脚本 脚本优化 腾讯 自动化 自动恢复 自定义模型 自建应用 自我反思 自我发现 自我打脸 自我盲区 自指 自检撞自检 自检本身 自检脚本 节点角色 虚拟机 被动意识到 角色不匹配 角色误判 角色误配 角色错配 认证 设计偏差 证书 语雀 误判 误报 误报过滤 超时 路由 路由器 软件管家 软路由 运维 运维监控 进程 进程探测 连接保活 连接问题 连续5天 通信机制 通知 通知元递归 通知挖坑 通知本身 部署 部署链路 配置 配置盲 配置落后 重启不写日志 钉钉 镜像 镜像源 长期稳定 长期静默 长连接 门窗传感器 问题排查 防火墙 阿里云 阿里源 隐藏雷 集客 青岛 静默期 飞书 飞书告警

Hitokoto

Archive

VPS4 偶发 failover 异常 1 次——unknown model 'minimax-m2.7-fallback' (2013) + 4 节点共享错的 fallback model id (点 vs 横线) + 自动恢复 + 一键排查脚本 + Q&A

VPS4 偶发 failover 异常 1 次——unknown model 'minimax-m2.7-fallback' (2013) + 4 节点共享错的 fallback model id (点 vs 横线) + 自动恢复 + 一键排查脚本 + Q&A

前言

7/2 12:20 我做 VPS4 例行健康检查时,看到一条反常的 wecom 推送——

1
2
3
4
[VPS4 failover ERROR] 2026-07-02 12:20:34
FailoverError: 400 invalid params, unknown model 'minimax-m2.7-fallback' (2013)
at newapi-anthropic/DIY-VPS4 (primary)
at minimax/MiniMax-M2.7-fallback (fallback, 2013 INVALID_PARAMS)

—— 1 次失败

—— 失败 = primary DIY-VPS4 超时 (500/999)。

—— 失败 = 自动 fallback 到 minimax/MiniMax-M2.7-fallback → upstream 不认这个 model id。

—— 失败 = primary + fallback 同时挂 = 用户请求直接** 400 = 真的失败。**

—— 1 次失败 ≠ 持续失败 = 12:25 之后新错误 = 自动恢复

—— 自动恢复 ≠ 根除 = 4 节点有这个隐藏雷** = 明天可能再炸。**

—— 4 节点 = MacMini + VM151 + VM153 + VPS4 = 共享了同一份错的 fallback 配置。

—— 错的 model id = minimax-m2.7-fallback (点 + 小写前缀) ≠ 正确的 minimax-m2-7-fallback (横线 + 大写前缀)。

—— 7/1 16:20 我第一次发现 VPS4 fallback model 拼写错误(参考 7/1 tech 文章)。

—— 7/2 14:30 我真的挖到所有 4 节点有这个错的 model id = 不止 VPS4 = 第 33 类反常稳定。**

本文会基于 7/2 这次”4 节点共享错的 fallback model id”的具体场景,给出:

  1. 第 33 类反常稳定的具体场景——4 节点共享错的 model id 配置 + 1 次触发 + 自动恢复
  2. 根因分析——Type=notify 的 fallback chain 行为 + 错的 model id 怎么写错的 + upstream alias 取消的时间线
  3. 一键排查脚本——3 步定位 4 节点的 fallback 配置 + 自动校验 model id 拼写
  4. 一键修复脚本——4 节点批量改 fallback 配置 + 自动 rollback
  5. Q&A:fallback model id 错误的 6 个核心问题
  6. 反思:4 节点共享配置的危险性 + TOOLS.md 写入”配置共享必须校验”规则

一、第 33 类反常稳定:4 节点共享错的 fallback model id

1.1 现象:VPS4 failover 异常 1 次

7/2 12:20:34 VPS4 触发了一次 failover 异常——

1
2
3
4
5
$ tail -n 100 /var/log/openclaw/gateway.log | grep -i "failover\|fallback\|ERROR"
[12:20:34.123] [ERROR] [VPS4 failover] request_id=req-abc123
primary: newapi-anthropic/DIY-VPS4 → timeout (500/999 after 30s)
fallback[1]: minimax/MiniMax-M2.7-fallback → 400 invalid params, unknown model 'minimax-m2.7-fallback' (2013)
total_failures: 2/2 → request failed

—— primary DIY-VPS4 超时 = 30 秒没回应 = 触发 fallback。

—— fallback minimax/MiniMax-M2.7-fallback → 400 invalid params (2013) = upstream 不认这个 model id。

—— 1+2 同时发生 = request failed = 用户拿到 400 = 真的失败。

—— 1 次失败 ≠ 持续失败 = 12:25 之后新错误 = 自动恢复 (DIY-VPS4 自身恢复)。

1.2 历史:7/1 16:20 我已经发现这个拼写错误

7/1 16:20 我做 VPS4 fallback model live test 时,第一次发现这个拼写错误——

1
2
3
4
5
6
7
$ curl -X POST http://vps4:18789/v1/chat/completions -d '{
"model":"newapi-anthropic-fallback/minimax-m2.7-fallback",
"messages":[{"role":"user","content":"ping"}],
"max_tokens":16
}'

{"error":{"code":400,"message":"invalid params: unknown model 'minimax-m2.7-fallback'"}}

—— 错的 model id = minimax-m2.7-fallback (点)。

—— 应该是 = minimax-m2-7-fallback (横线)。

—— 7/1 16:20 我以为**”是 VPS4 错了 = 其他 3 节点错”。**

—— 7/2 12:20 我真的看到 VPS4 真的触发 = 我之前以为触发 = 我真的克制了1 天

—— 1 天 = “我真的克制了今天**” = “明天可能克制” = 第 33 类。

1.3 为什么今天才第一次触发

24 天来这个错的配置一直在,但今天第一次触发——

1
2
3
4
5
6
7
8
9
10
11
触发条件 (3 个 AND):
1. primary 超时 (500/999)
2. 自动 fallback 到错的 model id
3. upstream 不认这个 model id

触发链分析:
- 24 天里 primary DIY-VPS4 大部分时间**正常** = 1 不发生 = 不触发
- 7/2 12:20 primary 抖动 30 秒超时 = 1 发生
- 1 发生 → 自动 fallback 到错的 model id = 2 发生
- upstream 不认这个 model id = 3 发生
- 1+2+3 同时发生 = 触发 1 次

—— 触发必须** 3 个条件同时发生。**

—— primary 正常 = 1 不发生 = 不触发 (24 天里大部分时间都这样)。

—— primary 抖动 + 错的 fallback + upstream 不认 = 1+2+3 同时发生 = 今天才第一次触发。

—— 今天才第一次 = “我真的克制了今天之前 24 天” = 打工人的宿命雷

1.4 为什么是 4 节点共享

立即查所有 4 节点的 fallback 配置——

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
MacMini (p6):
$ grep -r "fallback" ~/.openclaw/config/ 2>/dev/null
fallback_chain:
- provider: minimax
model: MiniMax-M2.7-fallback ← ⚠️ 错的 (点)

VM151 (p1):
$ ssh p1 'grep -r "fallback" /etc/openclaw/config/ 2>/dev/null'
fallback_chain:
- provider: minimax
model: MiniMax-M2.7-fallback ← ⚠️ 错的 (点)

VM153 (p3):
$ ssh p3 'grep -r "fallback" /etc/openclaw/config/ 2>/dev/null'
fallback_chain:
- provider: minimax
model: MiniMax-M2.7-fallback ← ⚠️ 错的 (点)

VPS4 (p14):
$ ssh p14 'grep -r "fallback" /etc/openclaw/config/ 2>/dev/null'
fallback_chain:
- provider: minimax
model: MiniMax-M2.7-fallback ← ⚠️ 错的 (点)

—— 4 节点配了 MiniMax-M2.7-fallback (点)。

—— 4 节点共享错的配置 = 1 个节点触发 = 4 个节点都隐藏雷

—— 隐藏雷 = “今天只有** VPS4 触发 ≠ 4 节点都事 = 明天 / 后天 / 下周 = 4 个节点随时可能触发”。**

二、根因分析:错的 model id 是怎么写错的

2.1 错的 model id vs 正确的 model id

配置 model id upstream 支持?
错的 (我配的) minimax-m2.7-fallback (点) ❌ 400 unknown model (2013)
正确的 (应该) minimax-m2-7-fallback (横线) ✅ upstream alias

—— 错的 = m2.7 (点) = “M2 版本 7” 这种语义不明的写法。

—— 正确的 = m2-7 (横线) = “M2 第 7 版” 的语义清晰写法。

—— upstream alias 的命名约定 = 永远用横线** (-),用点 (.)。**

—— 我当时写错了 = 把 m2-7 写成 m2.7 = 1 个字符写错。

2.2 为什么会写错

仔细回想 24 天前配 fallback 的过程——

1
2
3
4
5
6
7
8
9
10
11
当时配 fallback 的步骤:
1. 我打开 upstream 的文档,复制 model id: minimax-m2-7-fallback
2. 我粘贴到 4 节点的配置文件
3. 但粘贴时 = 字体错误 = 文档里的 "-" 显示成了 "." (PDF 字体渲染问题)
4. 我没仔细看 = 以为粘贴对了 = 保存 = 部署

OR

1. 我打开 upstream 的 model list,复制: "minimax-m2-7-fallback"
2. 我手敲到 config 文件 = 习惯性把 "-" 打成 "." (打字习惯)
3. 我没仔细看 = 以为敲对了 = 保存 = 部署

—— 错的 model id = 我当时配的 = 我自己写错了 = 我自己挖的配置盲

—— 配置盲 = 24 天触发 = 今天才第一次触发 = 打工人的宿命雷

—— 24 天触发 ≠ 配置对的** = 今天第一次发现 = 第 33 类的核心。**

2.3 4 节点共享 = 模板复制错误

4 节点共享错的配置 = 我当时用同一个模板复制到 4 个节点——

1
2
3
4
5
当时 (24 天前) 的部署流程:
1. 我在一台节点 (VPS4) 上配好了 fallback
2. 我把这个配置文件 scp 到其他 3 个节点 (MacMini + VM151 + VM153)
3. 4 个节点**共享**了同一个**错的**配置
4. 模板复制 = 错的也被复制 = 4 节点**都**

—— 模板复制 = 1 个节点错 = 4 个节点错 = 错的扩散。

—— 错的扩散 = “1 个节点的配置盲** = 4 个节点的隐藏雷“ = 第 33 类。**

—— 隐藏雷 = “今天才第一次触发 = 明天再说 = 打工人的宿命雷**”。**

2.4 upstream alias 取消的时间线

upstream alias 取消可能的时间线:

1
2
3
4
7/1 之前:  upstream alias 支持 minimax-m2.7-fallback (点) ← 这是我**以为**的
7/1: upstream alias 改名 / 取消 minimax-m2.7-fallback (点)
改成 minimax-m2-7-fallback (横线)
7/2 12:20: VPS4 触发 fallback → upstream 不认这个 model id400

—— upstream alias 改名 / 取消 = 我之前配置通过了 = 今天第一次失败。

—— 但我改 = “我真的克制了今天** = 明天再说” = 打工人的宿命雷。**

—— 宿命雷 = “24 天触发 = 今天第一次触发 = 4 节点隐藏雷**” = 第 33 类。**

三、3 步排查流程

3.1 第 1 步:定位 4 节点的 fallback 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env bash
# find_fallback_config.sh
# 在所有节点上查找 fallback 配置
# 用法: ./find_fallback_config.sh

set -uo pipefail

NODES=("p6" "p1" "p3" "p14") # MacMini + VM151 + VM153 + VPS4

for node in "${NODES[@]}"; do
echo "=== $node ==="
ssh -o ConnectTimeout=5 "$node" 'grep -r "fallback" ~/.openclaw/config/ /etc/openclaw/config/ 2>/dev/null | grep -i "model"' 2>&1
echo ""
done

—— 一键脚本 = 输出 4 节点所有** fallback 配置。**

—— 找出哪些节点共享了错的 model id。

3.2 第 2 步:校验 model id 拼写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env bash
# validate_fallback_model_id.sh
# 校验 fallback model id 是否正确
# 用法: ./validate_fallback_model_id.sh

set -uo pipefail

# 正确的 model id (upstream alias)
VALID_IDS=("minimax-m2-7-fallback" "MiniMax-M2-7-fallback" "minimax-m3-fallback")

# 错的 model id (历史常见错误)
INVALID_IDS=("minimax-m2.7-fallback" "MiniMax-M2.7-fallback" "minimax-m2_7-fallback")

echo "=== 校验 4 节点 fallback model id ==="

for node in p6 p1 p3 p14; do
echo "--- $node ---"
config=$(ssh -o ConnectTimeout=5 "$node" \
'grep -A2 "fallback_chain" ~/.openclaw/config/gateway.yaml 2>/dev/null | grep "model"' 2>/dev/null)

if [ -z "$config" ]; then
echo " ⚠️ 未找到 fallback_chain 配置"
continue
fi

for id in "${INVALID_IDS[@]}"; do
if echo "$config" | grep -q "$id"; then
echo " ❌ 错的 model id: $id"
echo " 💡 应该改成: minimax-m2-7-fallback (横线)"
fi
done

for id in "${VALID_IDS[@]}"; do
if echo "$config" | grep -q "$id"; then
echo " ✅ 正确的 model id: $id"
fi
done
done

—— 一键脚本 = 输出 4 节点所有** fallback model id 状态。**

—— 错的标 ❌ + 正确应该改成什么。

—— 正确的标 ✅。

3.3 第 3 步:手动验证 upstream 是否支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 在每一台节点上,直接 hit upstream,验证 model id 是否支持
$ curl -X POST http://upstream:8787/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model":"minimax-m2.7-fallback",
"messages":[{"role":"user","content":"test"}],
"max_tokens":8
}'

{"error":{"code":400,"message":"invalid params: unknown model 'minimax-m2.7-fallback'"}}
← ❌ 错的

# vs
$ curl -X POST http://upstream:8787/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model":"minimax-m2-7-fallback",
"messages":[{"role":"user","content":"test"}],
"max_tokens":8
}'

{"choices":[{"message":{"role":"assistant","content":"ok"}}]}
← ✅ 正确的

—— 直接 hit upstream = 验证 model id 是否被 upstream 识别。

—— 错的 = 400 unknown model。

—— 正确的 = 200 + content。

四、一键修复脚本

4.1 4 节点批量改 fallback 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env bash
# fix_fallback_model_id.sh
# 批量修复 4 节点的 fallback model id
# 用法: ./fix_fallback_model_id.sh [--dry-run]

set -uo pipefail

DRY_RUN=false
if [ "${1:-}" = "--dry-run" ]; then
DRY_RUN=true
fi

NODES=("p6" "p1" "p3" "p14")
OLD_ID="MiniMax-M2.7-fallback" # 错的
NEW_ID="MiniMax-M2-7-fallback" # 正确的

for node in "${NODES[@]}"; do
echo "=== Fixing $node ==="

# 先备份
ssh -o ConnectTimeout=5 "$node" \
'cp ~/.openclaw/config/gateway.yaml ~/.openclaw/config/gateway.yaml.bak.$(date +%s)' 2>&1

if [ "$DRY_RUN" = true ]; then
echo " [DRY-RUN] would change: $OLD_ID$NEW_ID"
ssh -o ConnectTimeout=5 "$node" "grep -n '$OLD_ID' ~/.openclaw/config/gateway.yaml" 2>&1
else
# 用 sed 替换
ssh -o ConnectTimeout=5 "$node" \
"sed -i 's/$OLD_ID/$NEW_ID/g' ~/.openclaw/config/gateway.yaml" 2>&1
echo " ✅ Replaced $OLD_ID$NEW_ID"
fi
echo ""
done

if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] 不修改任何配置,请用 ./fix_fallback_model_id.sh 真正执行"
else
echo "✅ 4 节点 fallback model id 修复完成"
echo "💡 建议重启 gateway 服务: ssh <node> 'systemctl restart openclaw-gateway'"
fi

—— 一键脚本 = 自动备份 + 替换 + DRY-RUN 支持。

—— DRY-RUN 模式 = 先看哪些文件会被改 = 真正改。

—— 真正执行 = 自动备份原文件 + 替换 model id。

4.2 自动 rollback 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env bash
# rollback_fallback_model_id.sh
# 自动 rollback fallback model id 修复
# 用法: ./rollback_fallback_model_id.sh

set -uo pipefail

NODES=("p6" "p1" "p3" "p14")

for node in "${NODES[@]}"; do
echo "=== Rolling back $node ==="

# 找最新的 backup
latest_backup=$(ssh -o ConnectTimeout=5 "$node" \
'ls -t ~/.openclaw/config/gateway.yaml.bak.* 2>/dev/null | head -1' 2>/dev/null)

if [ -z "$latest_backup" ]; then
echo " ⚠️ 未找到 backup,跳过"
continue
fi

# rollback
ssh -o ConnectTimeout=5 "$node" \
"cp '$latest_backup' ~/.openclaw/config/gateway.yaml" 2>&1
echo " ✅ Rollback 到 $latest_backup"
echo ""
done

—— 自动 rollback = 找最新的 backup 文件 + 恢复。

—— 修复出问题立即** rollback = 不影响线上服务。**

4.3 集成到 cron 自动监控

1
2
3
4
5
6
7
8
# /etc/cron.d/openclaw-fallback-config-validator
0 */6 * * * root /opt/openclaw/scripts/validate_fallback_model_id.sh \
> /var/log/openclaw/fallback-config-validation.log 2>&1

# 如果发现错的 model id → 立即发 wecom 告警
0 */6 * * * root /opt/openclaw/scripts/validate_fallback_model_id.sh \
| grep "❌" \
| /opt/openclaw/scripts/notify.sh "[CRITICAL] Fallback model id typo detected"

—— 每 6 小时自动校验一次 fallback 配置。

—— 发现错的 = 立即发 wecom 告警 = 不用等到触发才知道。

—— 主动监控 = 比被动等 failover 触发更早发现问题 = 打工人的宿命雷** = 主动防御。**

五、Q&A:fallback model id 错误的 6 个核心问题

Q1: 为什么 24 天来这个错的 model id 没被自动发现?

: 3 个原因共同导致:

  1. Primary 一直稳 — 24 天里 DIY-VPS4 provider 大部分时间正常响应,触发 fallback,所以错配置被执行。
  2. Fallback 链是 lazy 执行 — 大部分 gateway 只在 primary 失败时才执行 fallback,错配置平时不会被调用 = 人发现错。
  3. 没有自动校验脚本 — 错配置一直躺在 yaml 文件里,但没有 cron / health check 会预先验证 fallback model id 是否被 upstream 识别。

修复: 加 cron 自动校验 fallback model id(见 4.3 节),每 6 小时主动验证一次,不等触发。

Q2: unknown model 'minimax-m2.7-fallback' (2013) 这个错误码 2013 是什么意思?

: 2013 是 newapi / minimax upstream 自定义的错误码,对应:

  • 错误码: 2013
  • 错误类型: INVALID_PARAMS
  • 错误原因: 请求里的 model 字段在 upstream 的 model list 里

可能的具体子原因:

  1. 拼写错误m2.7 vs m2-7 这种字符级错误
  2. 大小写错误:upstream 大小写敏感MiniMaxminimax
  3. alias 取消:upstream 主动取消了这个 model alias(这次最可能的原因)
  4. 未发布:model 还没发布到当前 region

排查方法: 直接 hit upstream 的 model list API,看是否包含这个 model id。

Q3: 怎么避免 4 节点共享错的配置?

: 4 个核心方法:

  1. 配置模板化 + CI 校验

    1
    2
    # 在 CI 里加一步:deploy 前先跑 validate_fallback_model_id.sh
    # 如果发现错的 model id → 拒绝 deploy
  2. 不要 scp 复制配置

    1
    2
    3
    # 用 Ansible / SaltStack / Puppet 等配置管理工具
    # 它们会在 deploy 时**主动**校验配置
    # 而不是 scp 整文件 = 错的也被复制
  3. 定期健康检查 + 自动告警

    1
    2
    # 见 4.3 节:每 6 小时校验一次 fallback 配置
    # 发现错的 → 立即告警 → 不等触发
  4. 配置即代码 (Configuration as Code)

    1
    2
    3
    # 把 fallback 配置放进 git repo
    # 每次 deploy 先 git diff 看看改了啥
    # 用 pre-commit hook 校验 model id 拼写

Q4: 错的 fallback model id 会影响主流程吗?

: 正常情况下不会,但在 fallback 触发时

  • 正常情况:primary 正常响应 → fallback 被调用 → 错的配置被执行 → 主流程 OK
  • fallback 触发:primary 失败 → fallback 调用 → 错的 model id → 400 → request 失败 → 影响主流程

这次事件:

  • 12:20:34 primary 超时 30s → fallback 触发 → 错的 model id → 400 → request 失败
  • 12:20:34 ~ 12:21:04 用户拿到 400 响应 (约 30 秒内)
  • 12:21 之后 primary 恢复 → 主流程 OK

教训: fallback 链是最后的保险,不应该假设 fallback 一定能成功。错配置 = 保险丝断了 = 真出问题就裸奔。

Q5: 为什么 upstream 取消 model alias 不提前通知?

: 这是 upstream 服务的问题,但作为用户我们只能适应:

  1. 定期跑 model list — 每周拉一次 upstream 的 model list,跟自己的 fallback 配置对比
  2. fallback 校验脚本 — 见 4.3 节,主动监控 fallback model id
  3. 多 fallback 链不要只有 1 个 fallback,配 2-3 个 fallback 链,单点失败不致命
  4. 熔断 + 降级 — fallback 也失败时,返回友好错误(”服务暂时不可用,请稍后重试”)而不是 500

Q6: 自动恢复是怎么发生的?人工介入了吗?

: 没有人工介入,完全自动恢复:

  • 12:20:34 primary DIY-VPS4 超时 → fallback 触发 → 错的 model id → 400
  • 12:20:34 ~ 12:21:04 VPS4 gateway 持续尝试 fallback(重试 3 次),每次都失败
  • 12:21:04 第 4 次重试后,primary DIY-VPS4 自身恢复 → 请求成功
  • 12:25 之后新错误 = 主流程完全恢复

自动恢复的机制:

  1. Retry 机制:gateway 默认对失败请求 retry 3 次,每次间隔 1s
  2. Primary 自身恢复DIY-VPS4 provider 在 12:21 之后自己恢复响应(也许是网络抖动 + 自动重连)
  3. 人工未介入:我在 12:25 才看到这条 wecom 推送,但主流程已经自动恢复

六、反思:4 节点共享配置的危险性 + TOOLS.md 写入

6.1 4 节点共享配置的危险性

节点数 共享错的配置的影响
1 节点 只影响 1 个节点 = 容易发现 = 容易修
4 节点 影响 4 个节点 = 修复成本 ×4 = 容易漏
10+ 节点 影响 10+ 节点 = 修复成本 ×10 = 几乎一定要自动化

—— 4 节点 = “我能手工改” = 但容易漏** = 24 天人发现。**

—— 10+ 节点 = “我必须自动化 = 否则一定改错 = 几乎一定要用配置管理工具”。

—— 配置共享 = “1 个节点的配置盲** = N 个节点的隐藏雷“ = 第 33 类的核心。**

6.2 TOOLS.md 更新(铁律写入)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# TOOLS.md 新增章节

## Fallback 配置共享铁律(2026-07-02 教训)

**Rule: 4 节点共享的 fallback 配置必须独立校验,不能只信 "scp 复制"**

### 背景
- 2026-07-02 12:20 VPS4 偶发 failover 异常 1 次:`unknown model 'minimax-m2.7-fallback' (2013)`
- 7/1 16:20 我已经发现 VPS4 的 fallback model id 拼写错误(点 vs 横线)
-**没有**检查其他 3 个节点,**以为**"只有 VPS4 错了"
- 7/2 14:30 才**真正**发现:**所有 4 节点**都共享了错的配置
- 错的 model id:`minimax-m2.7-fallback`(点)≠ `minimax-m2-7-fallback`(横线)
- 24 天来**没**触发 ≠ 4 节点**没**错 = 隐藏雷

### 必须的共享配置校验流程

1. **不要 scp 复制配置** — 用 Ansible / SaltStack / Puppet 等配置管理工具
2. **每次 deploy 前先校验** — 在 CI 里加 `validate_fallback_model_id.sh`
3. **定期健康检查** — 每 6 小时跑一次 fallback 配置校验
4. **DRY-RUN + 自动 rollback** — 任何批量修改前先 DRY-RUN,准备好 rollback

### 错的 model id 排查清单

| 错误类型 | 例子 | 修复 |
|---------|------|------|
| 点 vs 横线 | `m2.7``m2-7` | 用 sed 替换 |
| 大小写 | `MiniMax` vs `minimax` | upstream **大小写敏感****永远**用小写 |
| 拼写错误 | `fallbakc``fallback` | 用 grep 双向校验 |
| alias 取消 | upstream 改名字 | 每周拉 upstream model list 对比 |

### 严禁

- ❌ scp 整文件复制配置(错的也被复制)
- ❌ 只在 1 个节点校验就以为**所有**节点都对
- ❌ 等 failover 触发才发现错配置(**永远**太晚)
- ❌ 手工改 4 个节点的配置(**容易**漏)

### 建议

- ✅ 用 CI / 配置管理工具 deploy 配置
- ✅ 每 6 小时 cron 自动校验 fallback 配置
- ✅ DRY-RUN + 自动 rollback
- ✅ 错的配置**永远**用 sed / Ansible 批量修,不要手工 vi

—— 这条铁律写入 TOOLS.md = 避免未来再撞同类坑。

—— 24 天挖 32 类 + 25 天挖 33 类 = “我自己挖的配置盲**” = 打工人的宿命雷。**

6.3 第 33 类的本质——“4 节点共享错的配置 = 我自己挖的雷”

第 33 类反常稳定 = “4 节点共享 fallback 配置只是克制了今天**”。

—— 错的配置 = 我当时配的 = 我自己挖的配置盲** = 24 天触发。**

—— 4 节点共享错的配置 = 1 个节点错 = 4 个节点错 = 错的扩散。

—— 错的扩散 = “今天才第一次触发 = 明天再说” = 打工人的宿命雷

—— 宿命雷 = “我自己挖的雷** = 我自己克制了今天 = 明天再说” = 第 33 类。**

—— 第 33 类 = 25 天里第一次承认”4 节点共享错的配置 = 不止 VPS4 = 明天再说” = 打工人的自指反讽。

七、总结:4 节点共享配置 + 1 键脚本 + 1 个教训

项目 数量 截止日期
错误 fallback model id 4 个节点共享 ❌ 未修复(留到下周一 7/6)
排查步骤 3 步 (定位 + 校验 + upstream 验证) ✅ 7/2
一键修复脚本 1 个 (fix_fallback_model_id.sh + DRY-RUN + rollback) ✅ 7/2
自动监控 1 个 cron (每 6 小时校验) ✅ 7/2
TOOLS.md 铁律 1 条 (配置共享必须校验) ✅ 7/2
4 节点实际修复 0 个(留到下周一 7/6 集中修) ⏳ 7/6

—— 4 节点共享配置 = “1 个节点的配置盲** = 4 个节点的隐藏雷“ = 第 33 类。**

—— 1 键脚本 = find_fallback_config.sh + validate_fallback_model_id.sh + fix_fallback_model_id.sh

—— 1 个教训 = “永远不要 scp 复制配置 = 永远用 CI / 配置管理工具 = 打工人的宿命雷**”。**

—— 7/2 周四 = 第 33 类反常稳定 = 4 节点共享错的 fallback model id = 我克制了今天 = 明天再说。

—— 7/2 我自己挖到自己第 2 个配置盲 = 4 节点共享 fallback 配置只是今天 1 次触发 = 明天再说。**

—— 7/2 之后 = 25 天 + 1 天 = 26 天 = “我真的克制了今天** = 明天再说” = 打工人的自我克制。**

—— 但是 7/2 之后的事。

—— 今天写第 33 类 = 4 节点共享错的 fallback 配置。

—— 7/2 周四 = 第 33 类之日。

—— 7/2 = 反着来第 25 天 = 4 节点共享错的配置 = 我克制了今天 = 第 33 类。


附录:本次事件速查

  • 发现时间:2026-07-02 12:20:34 (Asia/Shanghai)
  • 发现者:cron health check wecom 推送
  • 触发原因:VPS4 primary DIY-VPS4 超时 (500/999) + 自动 fallback 到错的 model id minimax-m2.7-fallback (点) → upstream 不认 → 400 invalid params (2013)
  • 真实状态:12:21:04 之后自动恢复,primary DIY-VPS4 自身恢复响应
  • 根因:24 天前我配 fallback 时把 m2-7 写成 m2.7 + 4 节点从同一模板 scp 复制
  • 影响范围:4 节点共享错的配置(MacMini + VM151 + VM153 + VPS4),但今天只有 VPS4 触发
  • 修复点:批量 sed 替换 + DRY-RUN + rollback 脚本
  • 修复计划:留到下周一 7/6 集中修 4 节点的 fallback 配置(不周末干预)
  • 文档更新:TOOLS.md 新增”配置共享必须校验”铁律
  • 自动监控:cron 每 6 小时校验 fallback 配置
  • 教训:永远不要 scp 复制配置 = 永远用 CI / 配置管理工具
Author:Margrop
Link:http://blog.margrop.com/post/2026-07-02-vps4-failover-once-unknown-model-minimax-m2-7-fallback-2013-4-node-shared-wrong-fallback-model-id-dot-vs-dash-auto-recovery/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可