最近我排查了一個很典型的通知系統問題。
表面上看,OpenClaw 的排程有正常執行,網站資料也抓得到,但 LINE 群組始終收不到通知。更容易誤導的是,部分 run 還顯示 ok,讓人一開始直覺懷疑是網站解析、排程設定,或 prompt 寫法出了問題。
但一路拆解之後,最後真正的根因不是 cron,也不是解析流程,而是 LINE 官方帳號本月免費訊息額度用完,導致主動發送持續被 429 擋下來。
這篇整理成一篇偏技術部落格風格的精簡版記錄,聚焦在排查順序、
問題現象
當時的需求很單純:
- 定時執行 OpenClaw cron
- 檢查某個公告頁面
- 若有新公告,就主動發送到 LINE 群組
實際看到的症狀是:
- cron 有跑
- 群組沒收到訊息
- 部分 run 顯示
ok - 部分 run 出現:
HTTPFetchError: 400 -HTTPFetchError: 429 -deliveryStatus: not-delivered
這類問題最麻煩的地方在於,系統看起來像只有一個問題,
第一步,先修排程邏輯,而不是先猜平台壞掉
這次最先修的不是 LINE,而是排程本身的送訊邏輯。
當時的設計同時存在:
- 任務內自己嘗試送訊
- 外層 delivery/announce 機制也可能介入
這會產生一個典型問題:
- 任務內若沒有真的送成功,只是輸出「原本應該送什麼」
- 外層又可能把這段內容當成待送摘要處理
結果就會變成:
- 看起來像有處理內容
- 但其實沒有真正送出
- 還可能多製造一層錯誤
所以第一個修正方向,是把送訊責任收斂成單一來源,
第二步,先保證資料一致性
下一步不是硬把通知送出去,而是先修正這件事:
送訊失敗時,不能把資料誤記成已送。
因此我重寫了排程 prompt,核心原則是:
1. 有新公告才處理
2. 只有確認送出成功後,才更新正式已送記錄
3. 如果送訊失敗或不確定是否送達:
NO_REPLY- 不更新記錄檔
- 不輸出「應送內容」摘要
這一步很重要,因為它把問題先從「可能污染資料」縮小成「
後來做受控測試也確認:
- 即使 LINE 發送失敗
- 正式記錄檔也不會被誤回寫
這代表資料一致性已經被保住。
第三步,確認問題不是「沒送」,而是「送了但失敗」
一開始最容易誤判的地方是:
- run 顯示
ok - 但群組沒收到訊息
這時候至少有兩種可能:
1. 任務根本沒送
2. 任務有送,但送失敗後被正確處理掉了
後來直接看 transcript 才確定:
- 任務內確實有嘗試送 LINE
- 但送訊時碰到
429 - 然後依規則
NO_REPLY - 所以 run 還是可能顯示
ok
這一步很關鍵,因為它把問題收斂成:
cron 執行流程沒壞,真正失敗的是 LINE 發送端。
第四步,從 log 看出這不是單一 job 問題
接著去看 gateway log,很快就發現不是只有某一支 cron 在失敗,而是整條 LINE 通道近期都反覆出現:
line final reply failed: HTTPFetchError: 429delivery-recovery: Retry failed for delivery ...: 429 -
這代表問題已經不能只看成單一排程故障,而要往更外層想:
- 是不是 LINE channel 本身有問題?
- 是不是平台在限流?
- 是不是有 backlog 不斷重試?
第五步,找到 delivery backlog 的實體位置
後來我把 backlog 的實體位置挖出來了:
~/.openclaw/delivery-queue/
裡面會留下待送但未成功的 delivery 項目,內容包含:
- 舊通知
- 測試訊息
- 失敗通知
再結合 log 可以看出:
- 某批 pending delivery 會被 delivery recovery 拿出來重試
- 重試又再次碰到
429 - 於是 queue 一直堆著、一直重試
這說明 backlog 的確存在,而且是跨時間持續存在。
第六步,隔離 backlog,但問題仍然存在
為了驗證 backlog 是否為主因,我採用比較保守的方式處理:
- 停 gateway
- 把 active queue 裡的 backlog 隔離到 quarantine 資料夾
- 再啟動 gateway
- 重新測試直接送訊
這種處理方式的好處是:
- 不直接刪資料
- 可回復
- 能快速驗證 backlog 是否造成主要影響
但測完後結果很清楚:
- backlog 確實被隔離了
- queue 也乾淨很多
- 但新的直接送訊仍然回
429
這表示 backlog 不是唯一問題,真正更底層的限制還在。
最後根因,LINE 免費訊息額度已用完
最後到 LINE Developers / 官方帳號後台確認,答案就很明確了:
- 本月免費訊息額度已經用完
這時整個事件才完整串起來:
1. 主動發送額度先耗盡
2. 所有新的主動送訊都回 429
3. 送失敗的內容堆進 delivery queue
4. recovery 不斷補送
5. 補送再度 429
6. 表面上看起來像 cron 有跑但訊息永遠送不到
所以最後真正的根因不是:
- cron 壞了
- 網站解析壞了
- OpenClaw 不會送 LINE
而是:
LINE 官方帳號的主動發送額度先耗盡了。
這次最值得保留的排查方法
這次事件裡,我自己覺得最值得保留的是下面這幾個排查習慣。
1. 不要把所有症狀都叫做「排程失敗」
要拆開看:
- cron 有沒有跑
- 任務邏輯有沒有走到送訊
- delivery 狀態是不是
not-delivered - gateway log 在報什麼
- backlog 有沒有持續重試
- 平台本身是不是有限流或額度問題
2. 先修資料一致性,再修通知能力
先保證「失敗不會誤標成已送」,再處理送訊本身,
3. ok 不等於真的送到
這次最迷惑人的點就在這裡。run status 只能代表任務流程是否成功收尾,不能單獨代表通知真的送達。
4. backlog 一定要找實體位置
只看 log 很容易一直猜。真的找到 queue 檔案位置後,整個問題才變得可驗證、可操作。
遇到類似問題時的排查順序
如果你也遇到「排程有跑,但通知沒到」的問題,
1. 確認 cron 是否真的有執行
2. 確認任務是否真的走到送訊邏輯
3. 確認失敗時不會誤回寫 sent 記錄
4. 查看 gateway log 裡的 delivery / 429 / 400 訊號
5. 檢查 delivery queue 是否有 backlog
6. 如果 backlog 清掉後仍然 429,就直接查平台額度或限流
這樣可以避免你一直在本地系統裡打轉,卻忽略真正的上游限制。
最後怎麼處理?
既然已確認這個月免費訊息額度用完,那在不付費前提下,
- 暫停主動 LINE 發送
- 排程改成只記錄待通知內容
- 同時輸出:
- 一份人類好讀的 Markdown 待通知檔
- 一份結構化 JSON 待通知檔
- 等下個月額度重置後,再切回主動推播
這樣做的好處是:
- 不再持續撞
429 - 不再製造新的 delivery backlog
- 不會遺失新公告資訊
- 下個月可以平順恢復
結語
這次的問題很適合提醒自己一件事:
排程有跑,不代表通知真的有送到。
很多時候,問題根本不在 cron,而是在更外層的 delivery 設計、queue 重試機制,甚至平台額度本身。
如果我只停在「cron 顯示 ok」這一層,就永遠找不到真正原因。
這次最後能收斂到 LINE 免費額度用盡,靠的不是單一招,而是一路把每一層拆開來看,
如果你也在做 OpenClaw、LINE 通知、或任何有 queue / retry / 平台額度限制的系統,我很推薦保留這種排查思路。

沒有留言:
張貼留言