Windows apply_patch can commit the target mutation but never return completion, making retry unsafe

Open 💬 1 comment Opened Jul 17, 2026 by Saigwanath

Summary

On Windows with @openai/codex 0.144.5, an apply_patch invocation created the intended scratch file with the exact expected SHA-256, but the tool call never returned a successful completion and was terminated after nonreturn.

This is a committed_without_completion state. Retrying automatically would be unsafe because the requested mutation is already durable even though no completion receipt exists.

Observed evidence

  • Platform: Windows x64.
  • Package: @openai/codex 0.144.5.
  • Component: codex.exe --codex-run-as-apply-patch.
  • Intended target: a non-sensitive scratch text file in a local test workspace.
  • Expected SHA-256: e27a32267b34b6ee0b9814b94569412f3c5366642c1ca4add23a899f74d2f0bf.
  • Actual SHA-256 after read-only reconciliation: e27a32267b34b6ee0b9814b94569412f3c5366642c1ca4add23a899f74d2f0bf.
  • Target visible and readable: yes.
  • Tool returned a successful completion: no.
  • A completion timeout/nonreturn was observed: yes.
  • Tool was terminated after nonreturn: yes.
  • Automatic retry performed: no.
  • Original attempt ID: not available.
  • Exact timeout duration: not recorded and intentionally not inferred.
  • No host completion/cancellation receipt was available after termination.

No private absolute paths, credentials, source contents, or repository data are included in this report.

Safety impact

The caller cannot distinguish a mutation that never happened from one that committed but lost its completion signal. A generic retry policy can therefore duplicate or conflict with an already-durable mutation.

The safe local response is to reconcile the exact target and expected hash read-only, classify the state as committed_without_completion, and prohibit automatic retry.

Requested host contract

  1. Allocate and expose a stable attempt ID before mutation begins.
  2. Return an explicit terminal state within a bounded caller-visible deadline.
  3. On timeout, expose exact-target read-only reconciliation fields: intended target/marker, expected hash, actual hash, visibility, and reconciliation time.
  4. Return committed_without_completion when expected bytes are present without a successful completion receipt.
  5. Return a cancellation/termination receipt and whether the child/tool remains alive.
  6. Mark timeout or ambiguous completion as non-retryable by default; require explicit operator action for another attempt.
  7. Correlate tool return, timeout, termination, and reconciliation events under the same attempt ID.

Related issues

  • #21117 covers duplicate approval and an app-server hang during an apply_patch sandbox retry path.
  • #20874 covers Windows command output loss and hanging after redirected output was written.

This report is related but distinct: the target mutation itself was durably committed with the expected hash while no successful apply_patch completion was delivered.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗