「同じ落とし穴で、なぜ3回も転ぶのか?——zidongjiexi 反復修復の実録(体系的な教訓付き)」
ある種のバグは思春期みたいなものだ。終わったと思ったら、髪型を変えてまた戻ってくる。
今回のastrbot_plugin_zidongjiexiplusyoutubexiazaiの不具合はまさに典型で、毎回「直ったように見える」のに、次の再起動でまた派手に転ぶ。
一、事故タイムライン:「PIL エラー」から「環境互換性」へ
フェーズ 1:初回のエラー(リソース読み込みでクラッシュ)
プラグイン起動時、render.py の _load_video_button で発火:
img.convert("RGBA")- Pillow 内部の
AssertionError
直観的な結論:media_button.png のリソースデコードに地雷がある(画像自体の異常の可能性もあれば、実行時のデコード経路の差異の可能性もある)。
フェーズ 2:1回目の修復(成功したように見える)
リソースを(RGBA で)再保存したところ、その場の再起動では動いた。
そのため「問題は解決した」と結論づけやすい。
フェーズ 3:再発(同じ箇所で再びクラッシュ)
その後の再起動でまた落ちた。つまり問題は「一度だけファイルが壊れていた」ではなく:
- そのコードパス自体にフォールトトレランスがない
- プラグインが異なる実行状態になると、同種のデコード失敗を引き起こし得る
フェーズ 4:2回目の修復で「修正が新たな問題を導入」
パッチ適用中に、典型的な二次事故が2種類発生:
- 構文レベルの混入(コメント/文字列処理の不備)で
SyntaxErrorを引き起こす - 環境ライブラリのバージョン差により
EmojiCDNSourceの引数が非互換(enable_tqdmが未対応)
最終的に、安定版ファイルへのロールバック + 最小増分の変更 + コンパイル検証 + 再起動検証で収束。
二、根本原因分析:なぜ「直したのにまた壊れる」のか
1)「一度成功」は「体系的修復」ではない
壊れた画像を再保存するのは、その場のサンプルを消しただけで、将来また同種のパスが発火しないことを保証しない。
2)プラグインコードに重要リソースのフォールバック戦略がない
リソースは外部依存だ。外部依存は「失敗しても致命傷にならない」べきである。
3)実行環境は不変ではない
同じプラグインでも、異なるコンテナイメージや依存バージョンでは初期化挙動が変わり得る。
今回の apilmoji の引数互換性差は、業務ロジックの誤りではなく、エコシステムのバージョン結合である。
4)修復プロセスに「強制ゲート」がない
パッチごとに次の強制検証を即時にやらないと、「動く状態」を「構文エラー状態」に簡単に戻してしまう:
py_compileの構文チェック- 最小コールドスタート検証
- 重要プラグイン読み込みログのアサーション
三、今回の最終修復でやったこと
zidongjiexi
_load_video_buttonにフォールトトレランスを追加:img.load()で事前に強制デコード- 例外時は透明プレースホルダー画像に降格し、プラグイン全体の起動失敗を回避
EmojiCDNSourceの非互換引数enable_tqdmを除去し、現行実行環境に適合- 複数回の再起動で検証し、プラグインが安定して「読み込み済み」状態に入ることを確認
memelite(ついでに修正)
libEGL.so.1欠如を修正:libegl1/libgl1をインストールし、起動期の依存復旧ループを解消
四、なぜ再起動が 20 秒から 600+ 秒に変わったのか
「マシンが急に遅くなった」のではなく、起動チェーンにブロッキング要因が増えた:
memeliteの依存復旧の反復 + pip プロセスで起動が長引く- プラグインの読み込み失敗の反復が全体初期化時間を増やす
- エラーログ嵐(第三者ログのフォーマット異常を含む)が体感の詰まりをさらに増幅
平たく言うと:AstrBot が一夜で老けたのではなく、起きながらタイヤを直している。
五、工学的教訓(バグ修正より価値がある)
教訓 1:リソース読み込みは「失敗しても生存」できなければならない
画像、フォント、外部 CDN、Cookie ファイル……常に健全だと仮定してはいけない。
耐障害性 + 降格が、プラグイン保守性の最低ラインである。
教訓 2:修正は「最小変更の閉ループ」で回す
推奨の固定手順:
- 既知の安定ベースラインに戻す
- 変更は1箇所だけ
- 構文/単点検証
- 再起動で検証
- 次の1箇所へ
教訓 3:「直った」は再現可能な検証を含めなければならない
「こっちでは直った」には意味がない。再起動を跨ぎ、時間を跨いでも「まだ直っている」を再現できなければならない。
教訓 4:ログを書くときは全体フォーマット結合に注意
サードパーティのログフィールドが自システムのログ形式と合わないと、ノイズになったり診断を妨害したりし得る。
サードパーティ logger に専用 handler を付ける、あるいはログレベルを下げることを検討するとよい。
六、今後の提案(次の再演を防ぐ)
- プラグインに起動時セルフチェックコマンドを追加(リソースファイル、依存バージョン、重要設定を検査)
- CI またはリリーススクリプトに追加:
- 構文チェック
- 最小 import テスト
- 重要初期化関数の smoke test
- 「非コア機能リソース」は統一して降格戦略を採用
- 「再起動所要時間の警告しきい値」(例:>90s で通知)を設け、遅い箇所のログを自動収集
結語
今回の事故で最も価値があるのは「ついに直った」ことではなく、次を確認できたことだ:
- 本当に納品できる修正とは、一度きりの手作業の消火ではない
- システムを「運で起動する」から「予測可能に起動する」へ変えることだ
バグが知能税を取りに来るのだとしたら、今回は少なくとも領収書を残せた。![]()