Skip to content

Codex プロバイダ: ストリーム切断時にバイナリ自身の再接続を中断してしまい、長尺レスポンスが回復できない #774

@nrslib

Description

@nrslib

背景

Codex プロバイダで長尺レスポンス(例: スライド60枚を1コールでレビュー)を実行すると、サーバ側がレスポンス完了前に websocket を切断することがある(websocket closed by server before response.completed)。

この切断時、codex バイナリ自身は最大5回の再接続(Reconnecting... N/5)を持っているが、TAKT 側がそれを活かせていない。実ログ(debug)で次が確認できた。

  • 終端は毎回 type:"error"("Reconnecting..." 通知)で、SDK 本来の終端シグナル turn.failed は1度も発生していない(error 9回 / turn.failed 0回)
  • 再接続カウンタは毎回 2/5 止まりで 3/5 以降に進まない
  • TAKT は error を終端扱いしてストリームを打ち切り、独自にスレッドを丸ごと再実行する retry(PR [#758] fix-codex-reconnect-retry #767)を最大8回・上限なしの指数バックオフ(合計約255秒)で繰り返した末に失敗する

調査の結果(Codex CLI にもセカンドオピニオンを取得)、TAKT が type:"error"(再接続中の通知)を「回復不能エラー」と誤認してストリームを break し、その結果 SDK の generator が閉じて codex の子プロセスが kill され、バイナリ自身の再接続が毎回途中で潰されていることが「再接続できない」真因と判断した。SDK 公式コンシューマ(Thread.run())は error を無視して読み続け、終端は turn.failed のみで扱っている点も裏付けになる。

(留保: SDK 型定義のコメントは ThreadErrorEvent を "unrecoverable" と記載しており、設計意図としては終端の可能性も残る。状態に基づく判定で安全に吸収する想定。)

やりたいこと

  • ストリーム中の error イベントだけでストリームを打ち切らず、codex バイナリ/SDK 自身の再接続を完遂させる
  • 終端の確定は turn.failed / 例外 / 外部 abort / idle-timeout に限定し、ストリームが正常終了したのに完了も有効な出力も無い場合のみ、記録しておいた最後のエラーで失敗とする
  • PR [#758] fix-codex-reconnect-retry #767 で入った「error 起点でスレッド丸ごと再実行する retry」は見直す(turn.failed・例外・idle-timeout 起点の retry は限定的に残す)
  • 併せて、再試行の指数バックオフに上限キャップを入れる(現状は上限なしで長尺タスク時に数分間フリーズして見える)

補足

  • 関連: PR [#758] fix-codex-reconnect-retry #767(Codex reconnect を retry 可能にした変更。層を取り違えており本件で置き換え検討)
  • 再現: 長尺レスポンスを出すエージェント(gpt-5.5 等)で、サーバ側 websocket 切断が起きるケース

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions