Codex Desktop terminal panel does not render on macOS 26.5.1 (M1 Max, arm64)

Resolved 💬 5 comments Opened Jun 14, 2026 by DougCrossDesign Closed Jul 26, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

The terminal panel does not render in Codex Desktop on macOS 26.5.1. The main UI loads normally, but the terminal panel never appears when selected.

Environment

  • macOS 26.5.1 (25F80)
  • Apple M1 Max
  • Codex Desktop 26.609.41114
  • arm64 build (confirmed via binary inspection)

Steps to reproduce

  1. Launch Codex Desktop
  2. Open or create a workspace
  3. Click the Terminal button/panel control

Expected behavior

A terminal panel opens within the workspace and is available for interactive command execution.

Actual behavior

No terminal panel is displayed. The application remains responsive, but the terminal never renders or initializes.

Troubleshooting performed

  • Full uninstall and reinstall of Codex Desktop
  • Removed all Application Support, caches, logs, preferences, helper services, and browser integration files
  • Confirmed clean state before reinstall
  • Verified arm64 binary via:

file "/Volumes/Codex Installer/Codex.app/Contents/MacOS/Codex"

  • Reset macOS permissions
  • Tested GPU-related launch flags (no improvement)

Additional observations

  • Codex CLI works normally
  • Codex Web works normally
  • Issue is reproducible after complete clean install
  • Appears isolated to Desktop UI terminal panel initialization

Possibly related issues

  • #26950
  • #26184

View original on GitHub ↗

5 Comments

github-actions[bot] contributor · 2 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #26950

Powered by Codex Action

DougCrossDesign · 2 months ago

Thanks — I believe this may be related but not identical.

My issue is reproducible on macOS:

  • macOS 26.5.1 (25F80)
  • Apple M1 Max
  • Codex Desktop 26.609.41114 (arm64)

Behavior differs slightly:

  • Terminal panel does not render at all (no flash or window)
  • Main UI loads normally
  • Issue persists after full clean install and removal of all application data

This may indicate a cross-platform terminal panel initialization issue rather than a platform-specific regression.

AliAltivate · 1 month ago

Reproduced on a substantially newer build; the issue persists.

Environment

  • Codex Desktop: 26.707.51957 (bundle build 5175)
  • Bundled Codex CLI: 0.144.0-alpha.4
  • macOS: 26.5 (build 25F5058e)
  • Architecture: Apple Silicon / arm64
  • Shell: zsh
  • Thread: local desktop thread with a writable folder-backed workspace

Reproduction

  1. Open a local Codex Desktop task.
  2. Select the terminal control / attempt to open the integrated terminal.
  3. The terminal panel does not become usable.
  4. Creating a new folder-backed task and fully restarting the app does not resolve it.

Diagnostic evidence

  • The task-facing terminal inspection API consistently returns:

``text
No app terminal session is attached to this thread yet.
``

  • A real persistent zsh PTY starts successfully in the same working directory and reaches a normal prompt.
  • An external terminal also works normally in the same directory; pwd resolves correctly.
  • Codex app-server processes are running.
  • A 30-minute unified-log query restricted to Codex messages containing terminal, pty, or shell returned no launch/attachment error.
  • Starting a persistent PTY did not change the task-facing state: it still reported that no app terminal session was attached.

This rules out a missing directory, broken zsh, or a general inability to allocate a PTY. The failure is in the Desktop UI/session attachment path.

Relationship to other reports

  • #29090 reports the panel flashing and immediately closing.
  • #31417 reports a visible, working terminal that read_thread_terminal cannot resolve because temporary and permanent thread ownership IDs diverge.

This reproduction matches #28141 at the UI level, while the backend symptom matches #31417. That combination suggests the panel-rendering failures and agent-side lookup failures may share a terminal-session ownership/attachment defect.

Working root-cause hypothesis

The terminal panel appears to depend on an attached terminal-session record. For a newly created or resumed thread, one of these transitions may be missing or racy:

  1. allocate PTY;
  2. register terminal session under the thread owner;
  3. canonicalize a temporary client-new-thread:... owner to the permanent thread ID;
  4. expose the terminal to both the UI panel and read_thread_terminal;
  5. render/persist the panel as open.

If step 2 or 3 fails, the UI may immediately collapse or render nothing because it sees no terminal model, while read_thread_terminal independently returns “no session attached.” The absence of an explicit log/error makes the state look like a harmless empty terminal rather than an attachment failure.

Suggested fix

  1. Use one canonical terminal owner key.
  • When a new thread receives its permanent ID, atomically migrate any terminal sessions registered under the temporary client ID.
  • Make both the panel store and read_thread_terminal resolve through the same canonical thread-ID function.
  1. Make panel visibility independent of successful PTY attachment.
  • Toggling the panel should keep it open.
  • While attaching, show a loading state.
  • If allocation/attachment fails, show an actionable inline error with a retry button instead of silently hiding the panel.
  1. Reconcile on thread resume/navigation.
  • On thread activation, look for terminal sessions owned by both the permanent ID and any known temporary predecessor.
  • Rebind the selected terminal session to the active route before rendering.
  1. Instrument the state transition.
  • Log sanitized fields for terminal_create_started, pty_created, terminal_registered, owner_rebound, panel_rendered, and terminal_attach_failed.
  • Include thread-ID kind (temporary/permanent), not the raw identifier.
  1. Add regression coverage.
  • New projectless thread: toggle terminal; panel remains visible and accepts input.
  • Folder-backed thread: same behavior.
  • Temporary-to-permanent thread-ID transition: existing terminal remains visible and readable.
  • Resume/navigation: terminal is rebound to the active thread.
  • Forced PTY/registration failure: panel remains visible with an error, never flashes closed.
  • End-to-end assertion: write a sentinel in the integrated terminal and confirm read_thread_terminal returns it.

The most useful invariant would be: if the UI exposes an enabled terminal toggle for a local thread, toggling it must result in either an attached terminal session or a visible error state—never a silent no-op/auto-collapse.

AliAltivate · 1 month ago

I traced this further against both the public openai/codex source and the shipped Desktop bundle. The failure is in the closed Desktop renderer, not the public app-server PTY implementation.

Exact findings

The Desktop terminal target resolver currently behaves like this (pseudocode preserving the shipped control flow):

switch (route.kind) {
  case "home":
    return terminalTarget(route.clientThreadId);

  case "local-thread":
    return terminalTarget(scope.clientThreadId);

  case "client-local-thread":
    return null;
}

That produces two distinct bugs:

  1. Panel does not open for client-local-thread. The resolver returns null, so the toggle path exits before creating a terminal tab or dispatching terminal-create. This precisely explains the silent no-op and the absence of terminal/PTY errors in unified logs.
  2. Agent lookup misses terminals after materialization. Terminal sessions remain indexed by clientThreadId, but read_thread_terminal first queries the terminal store with the permanent thread ID. The store therefore reports “No app terminal session is attached” even when a client-keyed session exists.

The same renderer already maintains both directions of the alias relationship when a client-created thread materializes:

  • client thread ID → permanent conversation/thread ID
  • permanent conversation/thread ID → client thread ID

So this does not require a new protocol or PTY migration.

Recommended minimal fix

1. Enable terminals for client-local routes

Handle client-local-thread exactly like the pre-materialized local route:

case "client-local-thread":
  return {
    conversationId: route.clientThreadId,
    conversationTitle: null,
    cwd: getThreadCwd(),
    hostId: getThreadHostId(),
  };

This ensures the toggle creates a tab and the terminal store dispatches terminal-create.

2. Resolve snapshots through the existing alias map

Keep live terminal sessions keyed by clientThreadId so they survive the client-local → permanent-local route transition. In read_thread_terminal, resolve in this order:

const permanentId = normalizeThreadId(threadId);
const clientId = clientThreadIdForConversation(permanentId);

const snapshot =
  terminalStore.getSnapshotForConversation(permanentId) ??
  (clientId == null
    ? null
    : terminalStore.getSnapshotForConversation(clientId));

Apply the same fallback inside the host-side thread-terminal-snapshot handler so renderer and host recovery paths agree.

3. Do not rekey or restart the PTY

Rekeying creates avoidable races with terminal listeners, pending writes, tab IDs, and active-session selection. Alias-aware lookup is smaller and preserves existing sessions through route materialization.

Regression tests

  1. client-local-thread: toggle terminal → tab remains open and terminal-create is dispatched.
  2. Materialize that thread → the same session ID remains active; no second PTY is created.
  3. Write a sentinel before and after materialization → both outputs remain in the same snapshot.
  4. Invoke read_thread_terminal using the permanent thread ID → sentinel is returned through alias lookup.
  5. Reload a permanent local thread → terminal toggle and snapshot lookup both work.
  6. Unknown thread ID → retain the current “No app terminal session is attached” result.

Why I did not open a PR

The public repository’s command/exec implementation already has integration coverage for PTY creation, input, streaming output, resize, and teardown. Its session key is intentionally connection + client-supplied process ID and it has no Desktop route/thread-owner concept. The relevant Desktop symbols (client-local-thread, terminal-create, and read_thread_terminal) are absent from the public repository, so a public backend PR would not fix this end-to-end.

I could not rerun the existing Rust PTY test locally because this machine lacks the repository-required Rust/just toolchain, but the source-level ownership boundary is unambiguous: the failing route resolver and snapshot alias lookup are both in the shipped Desktop renderer.

DougCrossDesign · 1 month ago

Recently downloaded and installed Version 26.721.41059, released Jul 24, 2026. The terminal window is now firing successfully.