← 事故一覧へ

minortool-misuseverifiedfirsthand

「停止」ボタンを押すと、ランチャーが自分自身を止めた

自作のアプリランチャーで、登録したダッシュボードの「停止」を押すと、ダッシュボードではなくランチャー自身が止まっていた。

発生日
深刻度
3/10
影響範囲
uptime
タグ
#lsof#process-management#port-forwarding#misdiagnosis#ai-coding

原因

停止処理は「そのポートを握っているプロセス」を lsof で探して止める設計だった。別の端末から使うための転送機能を足したとき、ランチャー自身の転送ソケットが同じポートを握るようになり、自分が停止対象に混ざった。

結果

ダッシュボードは止められず、ランチャーの画面は「サーバに繋がりません」になった。AIコーディングエージェントは最初、自分のツールセッションの後片付けで落ちたと誤診した。

対策

ポートを握るプロセスを探す関数で自分のPID(os.getpid())を必ず除外し、テストで固定した。待ち受けを増やしたら止める側も見直す。プロセスが消えたら、まず Terminated: 15 の有無を見る。

何が起きたか

自作のダッシュボード群を1枚の画面から起動・停止できる、自家用のアプリ ランチャーを作っていた。ある日、ローカル限定で動いているアプリを別の端末 (タブレット)からも使えるようにするため、素のTCPで中継する転送機能を足した。

その直後から、ダッシュボードの「停止」を押すと、止まるのはダッシュボードでは なくランチャーのほうになった。

現場の混乱

画面に出たのは「ダッシュボードが停止できない」「ランチャーのサーバに 繋がりません」という症状だった。押したボタンは効かず、押した画面そのものが 消える。

ここでAIコーディングエージェントは誤診した。最初に出した見立ては 「私のツールセッションの後片付けでランチャーが落ちた」というものだった。 ランチャーを起動したのがエージェント自身のシェルだったので、それらしく 聞こえる説明ではある。だが答えはターミナルに最初から出ていた。 Terminated: 15。誰かが明示的に SIGTERM を送った、という印だ。 送ったのはランチャー自身だった。

原因

このランチャーは、起動したアプリをPIDではなくポートで止める。子プロセスを 新しいセッションで起動する(ランチャーを閉じても道連れにしない)ため、 開き直すと「どれを自分が起こしたか」が分からなくなるからだ。そこで停止時は lsof で「そのポートを握っている者」を探して止める設計にしていた。

転送機能は、止めたいアプリと同じポート番号で待ち受ける。つまり、転送を 有効にした瞬間から、ランチャー自身も「そのポートを握っている者」になった。 停止ボタンは設計どおりに動き、設計どおりに自分を撃った。

待ち受けを増やす変更を入れたのに、「ポートを握る者を止める」側の前提を 見直していなかった。

対策

  • ポートを握るプロセスを探す関数(_pids_on_port)で、自分のPID (os.getpid())を必ず除外する。この挙動はテストで固定した。
  • 待ち受けを増やしたら、止める側も見直す。「このポートを握っているのは 止めたい相手だけか」を毎回問い直す。
  • プロセスが消えたら、推測を組み立てる前に Terminated: 15 の有無を見る。 それがあれば「誰かが止めた」のであり、落ちたのではない。

姉妹記事の agent-kill-by-port-overreach と同じく、「ポート番号で止める相手を 決める」やり方には、同じポートを握る別の誰かがいるという落とし穴がある。

出典

  • 運営者自身が起こした事故の一次記録です。外部に公開された元記事はありません。