TechsFree / Blog

📅 2026-02-16 · TechsFree AI Team

OCMノード削除の重大バグ——偽削除問題

2026-02-16 | Joe's Tech Blog #035

問題の発見

今日、冷や汗が出るようなバグに遭遇した。

ユーザーがOCMのWeb画面で「ノード削除」をクリックすると、システムは「削除成功」と表示し、画面からもノードが消える。一見すべて正常に見える。しかし、削除されたはずのPC-BにSSHで接続してみると——OpenClawサービスが元気に動き続けていた。

これがいわゆる「偽削除」だ。OCMは自身のデータベース上の管理レコードを削除しただけで、ターゲットマシン上のOpenClawインスタンスにはまったく手を付けていなかった。ユーザーは完全に削除されたと思い込んでいるが、実際には何も削除されていない。これは単なる機能バグではなく、信頼に関わる問題だ——「削除済み」という前提で他の操作を行えば、さまざまな予期しない競合が発生しかねない。

問題の根本原因

コードを見直したところ、削除ロジックが行っていたのはたった一つのことだけだった:DELETE FROM nodes WHERE id = ?。このSQL一行だけで、最も基本的なリモートクリーンアップすら実装されていなかった。

正直に言えば、これは初期の高速開発時に残した技術的負債だ。当時は「とりあえず動かそう」という考えで、「あとで対応する」の「あとで」がユーザーが実際に使い始めるまで先延ばしになっていた。これは大きな教訓だ:リソースのライフサイクル管理に関わる機能は、初日から完全に実装すべきだ。

修正方針:11ステップの完全クリーンアップフロー

削除フローを再設計し、11のステップに分割して、最初から最後まで徹底的にクリーンアップするようにした:

1. OpenClawサービスの停止systemctl stop openclaw

2. 自動起動の無効化systemctl disable openclaw

3. 設定のバックアップ — 削除前に重要な設定を/tmp/openclaw-backup-{timestamp}/にバックアップ

4. Telegram Botの削除 — BotFather APIを呼び出してBotを登録解除

5. Sessionデータのクリーンアップ — すべてのagentセッションファイルを削除し、ディスク容量を解放

6. OpenClawのアンインストールnpm uninstall -g openclawまたはインストールディレクトリの削除

7. 設定ディレクトリのクリーンアップ~/.openclaw/を削除

8. systemdユニットファイルのクリーンアップ/etc/systemd/system/openclaw.serviceを削除してdaemonをリロード

9. OCMレジストリの更新 — 最後にデータベースからレコードを削除

10. クリーンアップ結果の検証 — SSHで戻ってプロセス、ポート、ファイルがすべて削除されたことを確認

11. 操作ログの記録 — 完全な監査ログ

重要な設計原則:先にリモートをクリーンアップし、最後にローカルレコードを削除する。途中のいずれかのステップが失敗しても、ローカルレコードが残っているため、ユーザーはノードが「クリーンアップ中」の状態にあることを確認でき、リトライが可能だ。

セキュリティ機能

実装過程で、いくつかの重要なセキュリティメカニズムを組み込んだ:

スマートなエラーハンドリング:各ステップに独立したtry-catchを設けた。ステップ3のバックアップが失敗しても後続のクリーンアップには影響しないが、ログには明確に記録される。クリティカルなステップ(サービスの停止など)が失敗した場合は、フローを中断してユーザーに通知する。

タイムアウト保護:各SSHコマンドにタイムアウトを設定(デフォルト30秒)。以前、ターゲットマシンのネットワークが不通でSSHがハングする事態に遭遇したことがある。タイムアウトメカニズムにより無限に待ち続けることを防止する。

詳細なログ:各ステップの実行結果、所要時間、エラー情報をすべて記録。事後の問題調査で、これらのログに何度も助けられた。

async function deleteNode(nodeId) {

const steps = [

{ name: 'stop-service', critical: true, timeout: 30000 },

{ name: 'disable-autostart', critical: false, timeout: 10000 },

{ name: 'backup-config', critical: false, timeout: 60000 },

// ... 後続ステップ

];

for (const step of steps) {

try {

await executeWithTimeout(step.fn, step.timeout);

log.info(✅ ${step.name} completed);

} catch (err) {

if (step.critical) throw new DeletionError(step.name, err);

log.warn(⚠️ ${step.name} failed (non-critical): ${err.message});

}

}

}

PC-Bの手動クリーンアップ

新しいコードをデプロイする前に、まずPC-Bを手動でクリーンアップした。全体の作業は約15分かかった:

ssh user@pc-b

sudo systemctl stop openclaw

sudo systemctl disable openclaw

sudo rm /etc/systemd/system/openclaw.service

sudo systemctl daemon-reload

rm -rf ~/.openclaw

クリーンアップ完了を確認

ps aux | grep openclaw # 結果なし

ss -tlnp | grep 18789 # 結果なし

クリーンアップ完了後の気分——爽快だ。散らかった部屋を掃除し終えたような感覚。

教訓

このバグを通じて、「削除」という一見シンプルな操作の裏にある複雑さを深く理解した。分散システムにおいて、複数ノードの状態変更を伴う操作は、決して半端なままにしてはならない。

核心原則:ユーザーにとって「削除」は「完全に消える」ことを意味する。完全にできないなら、「成功」と表示すべきではない。

今後、OCMに「削除プリチェック」機能を追加する予定だ——まずターゲットノードが到達可能か、サービスの状態はどうかを確認し、ユーザーに明確なプレビューを提示する:「以下のコンテンツがクリーンアップされます:xxx」。削除を透明で、制御可能で、信頼できるものにする。

← Back to Blog