
WindowsのERROR_BROKEN_PIPE(109 / 0x6D)とは?原因・発生条件・アプリ側の正しい対処法
Windowsでパイプ通信(名前付きパイプ/匿名パイプ)を使っていると、突然 ERROR_BROKEN_PIPE (109 / 0x6D) に遭遇することがあります。ログには「The pipe has been ended.(パイプが終了した)」とだけ出て、原因がつかみにくいのが厄介です。
この記事では、ERROR_BROKEN_PIPEの意味を「どういう状態で起きるのか」という実務視点で整理し、再現しやすい典型パターン、アプリ側の安全な扱い方、調査の手順までまとめます。
- WindowsのERROR_BROKEN_PIPE(109 / 0x6D)とは?原因・発生条件・アプリ側の正しい対処法
ERROR_BROKEN_PIPE(109)の意味を一言でいうと
ERROR_BROKEN_PIPE は「通信路として使っていたパイプの片側が閉じられた(終端した)のに、もう片側が読み書きしようとした」ことを示すエラーです。
重要なのは、“壊れた”というより“相手が先に終了した/切断した” というニュアンスが強い点です。例えるなら、通話相手が先に電話を切ったあとに、こちらが話し続けてしまった状態です。
どんな場面で起きる?(発生条件の典型)
パイプはプロセス間通信(IPC)でよく使われます。次のような構成で頻出します。
-
親プロセスが子プロセスを起動し、標準入出力をパイプでつなぐ
-
サービスとクライアントを名前付きパイプで接続する
-
ワーカーとフロントエンドが匿名パイプで双方向通信する
ここで、相手側プロセスが終了したり、パイプハンドルをクローズしたりすると、残った側が WriteFile / ReadFile などを呼んだ瞬間に 109 が返ります。
よくある具体例
-
子プロセスがエラー終了し、標準入力(stdin)の読み取りをやめたのに、親が書き込みを続けた
-
クライアントが切断(CloseHandle)したのに、サーバーが返信を書き込もうとした
-
タイムアウトやキャンセル処理で接続を閉じた直後に、別スレッドが書き込みを実行してしまった(レース)
-
通信プロトコル上の終了手順が曖昧で、片側が先に閉じてしまう設計になっている
どのAPIで返りうる?
パイプに対する読み書きや接続に関わるWindows APIで返る可能性があります。代表例は次のとおりです。
-
WriteFile:相手が読む側を閉じたあとに書き込むと発生しやすい -
ReadFile:相手が書く側を閉じたあと、読み取りで検出されることがある -
その他パイプ操作系:接続状態やI/O状態に依存して返る
ポイントは、**「I/Oを実行した瞬間」**に露呈することが多い点です。接続が切れていても、次の読み書きまで気づけないケースがあります。
ERROR_BROKEN_PIPEは「異常」か「通常の切断」か
ここが設計で最重要です。ERROR_BROKEN_PIPEは、状況によっては**“正常な切断”として扱うべき**ことがあります。
-
クライアントがユーザー操作で終了 → サーバー側は「切断された」として静かに終了処理へ
-
子プロセスが処理完了で終了 → 親プロセス側は「通信完了」として後始末へ
一方で、期待していないタイミングで頻発するなら、以下の可能性が高いです。
-
相手が例外・クラッシュ・強制終了している
-
プロトコル(メッセージ境界、終端、フラッシュ)が合っていない
-
多スレッドでクローズとI/Oが競合している
-
バッファ枯渇やデッドロック回避のために相手が先に閉じている
「切断=バグ」と決めつけず、**“なぜそのタイミングで相手が閉じたのか”**を切り分けるのが近道です。
開発者が取るべき基本の対処(安全な型)
ERROR_BROKEN_PIPEに遭遇したときの基本方針はシンプルです。
-
そのパイプのI/Oを継続しない(以降の読み書きは失敗する前提)
-
開いているハンドルを閉じる(CloseHandle等)
-
関連リソースを解放(バッファ、イベント、スレッド、キューなど)
-
上位へ“切断”として通知(再接続するか、処理を終えるか判断)
「リトライすれば直る?」の落とし穴
通信相手がすでにいない以上、同じハンドルでのリトライは基本的に無意味です。必要なのは、
-
再接続(新しい接続を張り直す)
-
セッション再確立(認証・初期化からやり直す)
-
処理を正常終了へ遷移(相手終了が想定内なら)
のいずれかです。
再発防止の設計ポイント
ERROR_BROKEN_PIPEを「起きても困らない」状態にするには、次の設計が効きます。
1) 終了手順(シャットダウン・プロトコル)を決める
片側が突然Closeするのではなく、
-
「これで最後のメッセージ」
-
「これ以降送らない」
-
「ACKを受けたらクローズ」
のように、終了を合図するメッセージを設けるとレースが減ります。
2) クローズとI/Oの競合(レース)を潰す
多スレッド構成では、片方のスレッドがCloseした直後に別スレッドがWriteして109になる、が典型です。対策としては、
-
クローズ前に送信スレッド停止→完了待ち→Close
-
ハンドルの生存管理(参照カウント、状態フラグ、ロック)
-
キャンセルAPIやイベントでI/Oを中断してから終了
など、終了処理を手順化します。
3) エラーを「切断イベント」として扱う
例外扱いでログが赤くなるだけだと運用が辛くなります。想定内の切断なら、
-
INFOレベルで「peer disconnected」
-
メトリクスとして切断回数を記録
-
同時に相手の終了コードや直前のメッセージを残す
といった形で、運用可能な情報に落とし込みます。
調査のコツ(ログで見るべきポイント)
原因を掘るときは、エラー109そのものよりも「直前の流れ」が重要です。
-
109が出たI/Oは
ReadかWriteか -
その直前に相手へ何を送ったか(サイズ、コマンド種別)
-
直前にClose/Disconnect/Cancel相当の処理が走っていないか
-
相手プロセスの終了コード、クラッシュログ、イベントログ
-
同時刻にタイムアウトや再接続処理が走っていないか
特に、「一見ランダムに見える109」は、タイミング競合が原因のことが多いです。スレッドや非同期I/Oが絡む場合は、状態遷移のログ(Connected→Closing→Closed)を明示的に入れると特定が一気に楽になります。
関連エラーとの関係を押さえる
パイプ周りでは似た意味合いのエラーが近くに出ます。これらは「通信状態の違い」を示すヒントになります。
-
ERROR_PIPE_CONNECTED:接続状態に関するイベントで出ることがある -
ERROR_NO_DATA:相手の状態やI/O条件によっては、109の代わりに出ることがある -
ERROR_HANDLE_EOF:終端扱いとして出ることがある
同じ“切断”でも返り方が揺れることがあるので、アプリ側は 「切断系の戻り値をまとめて扱う」 設計にしておくと堅牢です。
まとめ:ERROR_BROKEN_PIPE(109)は「相手が先に閉じた」サイン
ERROR_BROKEN_PIPEは、パイプ通信で相手側が終了・切断し、こちらが読み書きしようとして初めて検出される典型的なエラーです。対処は「その接続は終わった」とみなしてハンドルを閉じ、リソースを解放し、必要なら再接続へ進めること。
そして再発防止の要点は、終了手順の明確化と、クローズとI/Oの競合を潰す状態管理です。109を“例外”として恐れるより、切断イベントとして設計に組み込むことで、運用も実装も一段と安定します。