Skip to content

Markdown preview tab becomes unresponsive after Codex edits the file #33160

Open

Description

@azolotov

What version of the Codex App are you using (From “About Codex” dialog)?

26.707.72221

What subscription do you have?

Pro

What platform is your computer?

Darwin 25.5.0 arm64 arm

What issue are you seeing?

When a Markdown file is open in rendered/rich-text preview and Codex edits the file, that preview tab becomes unresponsive. Clicking the tab's close button does nothing. The tab remains stuck until I switch to a different Codex task and then switch back, after which the tab can be closed normally. The rest of the app remains responsive enough to switch tasks.

What steps can reproduce the bug?

  1. Open a Markdown file in the Codex Desktop file viewer.
  2. Switch the file to rendered/rich-text preview.
  3. Ask Codex in the current task to edit that Markdown file.
  4. After Codex applies the edit, click the preview tab's close button.
  5. Observe that nothing happens and the tab cannot be closed.
  6. Switch to a different Codex task, then switch back to the original task.
  7. Click the tab's close button again; it now closes normally.

What is the expected behavior?

The Markdown preview tab's controls should remain responsive after Codex edits the underlying file. Clicking the close button should immediately close the tab without requiring a task switch.

Additional information

Workaround: switch to a different Codex task and then back to refresh the tab state.

This is distinct from #32571: the problem is not the preview switching to source/diff mode or losing scroll position. The tab's close action itself stops responding after the edit.

Activity

  1. added
    appIssues related to the Codex desktop app
    on Jul 14, 2026
  2. Oct-2300 commented on Jul 21, 2026

    @Oct-2300

    I can reproduce this on the Windows desktop app, and the production bundle plus logs point to a stale onBeforeClose guard rather than a missed pointer event.

    Environment

    • Microsoft Store ChatGPT/Codex app: 26.715.8383.0
    • Embedded frontend: 26.715.61943
    • Windows 11 x64
    • Native local Windows host (not WSL or VMOS)
    • A generated/local Markdown file opened in the right-side file viewer

    Evidence

    1. The desktop breadcrumb records the actual click on the Markdown tab's close button, including the button aria-label. The click reaches the renderer.
    2. The generic tab controller correctly supports closing index 0 and the final remaining tab: after removing the tab, an empty tab array closes the panel. The close is blocked before that reducer path.
    3. In the shipped review-file-source-tab-CDePGYBR.js, the file editor lifecycle represented by MF effectively does this when the file is dirty:
    const capturedSave = saveRef.current;
    onBeforeClose: dirty
      ? () => {
          capturedSave?.().then(saved => {
            if (!saved) return;
            clearGuard();
            closeTab();
          });
          return false;
        }
      : undefined;

    This creates permanent blocking states:

    • If saveRef.current was null when the guard was installed, optional chaining does nothing but the guard still returns false forever.
    • If saveUntilClean() resolves false or rejects (load/write/conflict/read-only/path failure), the guard is not removed and there is no retry/discard path.
    • Editor cleanup unregisters the save callback but does not remove an already installed guard.
    • The JF open/reopen path unconditionally carries existing?.onBeforeClose into the new descriptor, so a guard from an old editor lifecycle can survive reopening.

    In this reproduction, the same file open also hit the Windows path-normalization error tracked in #32474: a native path became /C:/..., and fs/watch failed with AbsolutePathBuf deserialized without a base path. That failure can make the save/load path return false and expose the stale guard.

    After the failed close, the panel remained mounted. The log recorded 73 additional ResizeObserver loop completed with undelivered notifications messages over the next several minutes, along with high renderer CPU/memory churn (working set peaked around 1.59 GB). Chinese IME input became effectively unresponsive while the renderer was saturated. This confirms severe layout/render churn, though it is not enough by itself to prove a monotonic JS heap leak.

    Suggested fix

    • Read saveRef.current when the close guard runs, not when dirty state installs it.
    • On missing save callback or load error, remove the guard and provide an explicit close path.
    • On save false/rejection, show Retry / Discard and close / Cancel; never leave a no-exit false guard.
    • Remove an owned guard on onSaveChange(null) and editor cleanup.
    • Track guard identity plus an editor/save epoch so an old cleanup or Promise cannot clear/close a reopened tab.
    • Deduplicate concurrent close attempts.
    • Do not inherit onBeforeClose across reset, path/host change, or a new editor lifecycle.
    • Disconnect the DetailPanel's observers and cancel any pending RAF during unmount.

    No raw logs are attached because they contain local workspace and task metadata. I can provide targeted sanitized excerpts if maintainers need them.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions