Skip to content

[#758] fix-codex-reconnect-retry#767

Merged
nrslib merged 1 commit into
mainfrom
takt/758/fix-codex-reconnect-retry
May 29, 2026
Merged

[#758] fix-codex-reconnect-retry#767
nrslib merged 1 commit into
mainfrom
takt/758/fix-codex-reconnect-retry

Conversation

@nrslib
Copy link
Copy Markdown
Owner

@nrslib nrslib commented May 28, 2026

Summary

背景

PR #752 の review-fix 実行中、npm test / npm run build / npm run lint は成功した後、npm run test:e2e:mock 実行中に workflow が abort した。

ログ上のエラー:

[DEBUG] [codex-sdk] Stream error event
Status: error
[ERROR] Error: Reconnecting... 2/5 (timeout waiting for child process to exit)
failureCategory: provider_error

セッションログ例:

/Users/nrs/work/git/takt-worktrees/20260523T1327-fix-run-directory-reuse/.takt/runs/20260523-132655-pr-752-pr-752-751-add-image-at/logs/20260523-222709-t0nu3i.jsonl

問題

Reconnecting... 2/5 という表示は、Codex SDK 側では再接続途中または transient failure に見える。一方で TAKT はこれを最終的な provider_error として扱い、workflow を abort している。

その結果、実際には E2E の成功/失敗が未確認なのに、タスク全体が失敗扱いになる。

暫定調査

確認した実装:

  • src/infra/codex/client.ts
    • CODEX_RETRYABLE_ERROR_PATTERNSstream disconnected before completion, transport error, network error, fetch failed などはある。
    • しかし Reconnecting...timeout waiting for child process to exit は含まれていない。
    • shouldRetry()provider_error の場合、isRetriableError(failure.reason) に一致したときだけ retry する。
  • e2e/helpers/takt-runner.ts
    • isTransientProviderFailure()Reconnecting... を transient provider failure として retry 対象にしている。

つまり、E2E helper では Reconnecting... を transient と見なしているが、本体の Codex provider retry 判定では同じ文字列を retryable と見なしていない。

暫定的な原因は、Codex SDK が Reconnecting... N/M (timeout waiting for child process to exit) をエラーとして返した際、TAKT 側がその文言を retryable transient failure として分類できず、通常の provider_error として abort していること。

ただし、SDK が Reconnecting... 2/5 を途中イベントとして返しているのか、最終エラー文言として返しているのかは未確認。SDK 側の仕様またはイベント列を追加確認する必要がある。

期待する挙動

  • Codex SDK の Reconnecting... N/M 系エラーを transient provider failure として扱う。
  • N < M のように途中再接続に見える文言の場合、少なくとも即 abort ではなく retry 判定または診断改善を行う。
  • retry しない場合でも、最終ログで以下を明示する。
    • provider reconnect failure であること
    • 実行中だった tool / command
    • その command の結果は未確認であること

受け入れ条件

  • src/infra/codex/client.ts の retry 判定で、Reconnecting... または timeout waiting for child process to exit を含む Codex SDK error が transient provider failure として扱われる。
  • retry 可能な場合は既存の Codex retry policy に従って再試行される。
  • retry 不能な場合でも、ユーザーに「テスト失敗」ではなく「provider reconnect failure / command result unknown」と分かる診断が出る。
  • Reconnecting... 2/5 (timeout waiting for child process to exit) を含む unit test を追加する。
  • 既存の rate limit 判定とは混同しない。

Execution Report

Workflow takt-default completed successfully.

Closes #758

Summary by CodeRabbit

Release Notes

  • セキュリティ強化

    • エラーメッセージと診断情報に含まれるトークン、APIキー、パスワードなどの機密情報が自動的に [REDACTED] にマスクされるようになりました。
  • バグ修正

    • 再接続失敗時の診断メッセージがより詳細で正確になり、トラブルシューティングが容易になりました。

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

Codex SDK の "Reconnecting..." エラーを transient provider failure として再分類し、再接続失敗時に実行中ツール・コマンド情報をマスク済みで診断する。StreamHandler でアイテムID・ツール追跡を拡張し、機密テキスト マスキングを共有ユーティリティ化する。

Changes

Codex 再接続エラー ハンドリング統合

Layer / File(s) Summary
共有機密テキスト マスキング実装
src/shared/utils/sensitiveText.ts, src/features/tasks/execute/traceReportRedaction.ts, src/__tests__/shared-utils-exports.test.ts
APIキー・トークン・認証情報を複数の正規表現で検出して [REDACTED] に置換する sanitizeSensitiveText() を共有ユーティリティとして新規実装。既存の traceReportRedaction.ts のローカル実装から移行し、バレルからは非エクスポート。
StreamHandler ツール追跡・アイテムID 拡張
src/infra/codex/CodexStreamHandler.ts, src/__tests__/codex-stream-handler.test.ts
StreamTrackingStateanonymousItemIdsactiveTool を追加し、安定したアイテムID と実行中ツール情報を追跡。emitCodexItemStart/Completed/Update で統一的に resolveCodexItemId() を使用し、ツール記録・クリア・ID リリースを管理。active tool tracking が callback 非依存で機能することを検証。
Codex クライアント 再接続判定と診断
src/infra/codex/client.ts
CODEX_RECONNECT_ERROR_PATTERNS を追加して "Reconnecting..." エラーを判定。再接続失敗時に実行中ツール・コマンドを sanitizeSensitiveText() でマスクしつつ、「実行中 command の結果不明」を示す finalFailure 診断を生成。retry 可能・不可の両経路で適用。
再接続リトライと診断 包括テスト
src/__tests__/codex-client-retry.test.ts
turn.failed/stream error/例外経路での再接続 1 秒リトライ、リトライ可能判定、リトライ不能時の「実行中コマンド結果不明」診断、および Authorization/Cookie/APIキー等の機密値が [REDACTED] にマスクされ元値が結果に残らないことを 119 個の具体例で検証。

Sequence Diagram(s)

sequenceDiagram
  participant CodexClient
  participant StreamHandler as CodexStreamHandler
  participant TrackingState as StreamTrackingState
  participant ErrorCheck as Error Detection
  participant Diagnosis as Diagnosis Builder
  
  CodexClient->>StreamHandler: emitCodexItemStart(item, ...)
  StreamHandler->>TrackingState: resolveCodexItemId(item)
  TrackingState-->>StreamHandler: stable id
  StreamHandler->>TrackingState: recordToolUse(toolData)
  StreamHandler->>CodexClient: tool_use イベント発行
  
  Note over CodexClient: ストリーミング中
  CodexClient->>ErrorCheck: "Reconnecting... 2/5" エラー
  ErrorCheck-->>CodexClient: isReconnectError() → true
  CodexClient->>CodexClient: 1秒後に retryPolicy で再試行
  
  Note over CodexClient: リトライ不能
  CodexClient->>Diagnosis: applyReconnectDiagnosis(failure, activeTool)
  Diagnosis->>Diagnosis: sanitizeSensitiveText(command)
  Diagnosis-->>CodexClient: finalFailure w/ masking
  CodexClient->>CodexClient: emitResult(finalFailure, category)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR タイトルは issue #758 と fix-codex-reconnect-retry を示しており、linked issue で要求されている Codex の再接続エラー対応の主要な変更を明確に反映している。
Linked Issues check ✅ Passed PR はリンク issue #758 の全ての受け入れ条件を満たしている:Reconnecting パターンと timeout パターンの retry 判定追加、実行中コマンド診断メッセージ実装、機密情報マスキング、及び包括的なユニットテスト追加。
Out of Scope Changes check ✅ Passed 全ての変更は issue #758 の要件に直接関連している。新規ユーティリティ関数、テスト拡張、及び client.ts の修正はすべて再接続エラー対応の実装スコープ内である。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch takt/758/fix-codex-reconnect-retry

Comment @coderabbitai help to get the list of available commands and usage tips.

@nrslib nrslib marked this pull request as ready for review May 29, 2026 02:14
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/__tests__/codex-client-retry.test.ts`:
- Around line 429-433: The test currently asserts fragments using multiple
expect(...).toContain calls which can miss regressions; instead build the full
expected diagnostic phrase (e.g., combine CODEX_RECONNECT_FAILURE_MESSAGE with
the provider/tool/command/result text into one string) and assert against
result.content with a single precise check (e.g., const expected =
`${CODEX_RECONNECT_FAILURE_MESSAGE} - provider reconnect failure; Active tool:
Bash; Bash command: npm run test:e2e:mock; Command result: unknown` and then
expect(result.content).toContain(expected) or
expect(result.content).toMatch(expected) / toEqual(expected) depending on exact
formatting), and apply the same change for the similar assertions around lines
472-476 to validate the entire contract phrase rather than partial fragments.

In `@src/infra/codex/client.ts`:
- Around line 132-146: Replace the current two separate diagnostic lines with a
single fixed, final-observable contract string "provider reconnect failure /
command result unknown" and ensure any active tool and its sanitized command are
appended in a stable, non-breaking way; update the block that builds the lines
array (see symbols: lines, activeTool, sanitizeSensitiveText) to always push one
final entry that begins exactly with "provider reconnect failure / command
result unknown" and then, if activeTool exists, append " - Active tool: <tool>"
and, if a Bash command string exists, append " - Bash command: <sanitized
command>" so the final log line is a single explicit phrase followed by the
active tool/command context without changing other log formats.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 872db9d2-aab7-4e15-bf48-5be3ccbbcd14

📥 Commits

Reviewing files that changed from the base of the PR and between 82fb846 and 6a78d7c.

📒 Files selected for processing (7)
  • src/__tests__/codex-client-retry.test.ts
  • src/__tests__/codex-stream-handler.test.ts
  • src/__tests__/shared-utils-exports.test.ts
  • src/features/tasks/execute/traceReportRedaction.ts
  • src/infra/codex/CodexStreamHandler.ts
  • src/infra/codex/client.ts
  • src/shared/utils/sensitiveText.ts

Comment on lines +429 to +433
expect(result.content).toContain('provider reconnect failure');
expect(result.content).toContain(CODEX_RECONNECT_FAILURE_MESSAGE);
expect(result.content).toContain('Active tool: Bash');
expect(result.content).toContain('Bash command: npm run test:e2e:mock');
expect(result.content).toContain('Command result: unknown');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

診断契約は部分一致ではなく契約文言そのものを検証してください

現在の assertion は断片的な toContain の組み合わせで、最終契約文言の退行を取りこぼします。provider reconnect failure / command result unknown の完全フレーズを直接検証してください。

💡 Proposed fix
     expect(result.content).toContain('provider reconnect failure');
+    expect(result.content).toContain('provider reconnect failure / command result unknown');
@@
     expect(result.content).toContain('provider reconnect failure');
+    expect(result.content).toContain('provider reconnect failure / command result unknown');

As per coding guidelines, "Confirm tests validate the post-fix behavior (not merely the presence of strings): unit tests should cover both normal and exception paths and assert the required final diagnostic semantics."

Also applies to: 472-476

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/__tests__/codex-client-retry.test.ts` around lines 429 - 433, The test
currently asserts fragments using multiple expect(...).toContain calls which can
miss regressions; instead build the full expected diagnostic phrase (e.g.,
combine CODEX_RECONNECT_FAILURE_MESSAGE with the provider/tool/command/result
text into one string) and assert against result.content with a single precise
check (e.g., const expected = `${CODEX_RECONNECT_FAILURE_MESSAGE} - provider
reconnect failure; Active tool: Bash; Bash command: npm run test:e2e:mock;
Command result: unknown` and then expect(result.content).toContain(expected) or
expect(result.content).toMatch(expected) / toEqual(expected) depending on exact
formatting), and apply the same change for the similar assertions around lines
472-476 to validate the entire contract phrase rather than partial fragments.

Comment thread src/infra/codex/client.ts
Comment on lines +132 to +146
const lines = [
'provider reconnect failure',
`Original error: ${failure.reason}`,
];
if (activeTool) {
lines.push(`Active tool: ${activeTool.tool}`);
const command = activeTool.tool === 'Bash' && typeof activeTool.input.command === 'string'
? activeTool.input.command
: undefined;
if (command) {
lines.push(`Bash command: ${sanitizeSensitiveText(command)}`);
}
}
lines.push('Command result: unknown');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

最終診断の契約文言を単一フレーズで明示してください

現状は provider reconnect failureCommand result: unknown が分離されており、要求される観測可能契約文言(provider reconnect failure / command result unknown)をそのまま明示していません。文言は固定で1行入れておくのが安全です。

💡 Proposed fix
-    const lines = [
-      'provider reconnect failure',
-      `Original error: ${failure.reason}`,
-    ];
+    const lines = [
+      'provider reconnect failure / command result unknown',
+      `Original error: ${failure.reason}`,
+    ];
@@
-    lines.push('Command result: unknown');

As per coding guidelines, "Treat error message/log text and retry/diagnostic wording as observable contracts: ensure the required final log explicitly states “provider reconnect failure / command result unknown”, includes the active tool/command, and does not accidentally alter unrelated logging formats."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/infra/codex/client.ts` around lines 132 - 146, Replace the current two
separate diagnostic lines with a single fixed, final-observable contract string
"provider reconnect failure / command result unknown" and ensure any active tool
and its sanitized command are appended in a stable, non-breaking way; update the
block that builds the lines array (see symbols: lines, activeTool,
sanitizeSensitiveText) to always push one final entry that begins exactly with
"provider reconnect failure / command result unknown" and then, if activeTool
exists, append " - Active tool: <tool>" and, if a Bash command string exists,
append " - Bash command: <sanitized command>" so the final log line is a single
explicit phrase followed by the active tool/command context without changing
other log formats.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Codex SDK の Reconnecting 系エラーを transient provider failure として扱う

1 participant