Skip to content

Multiple sidebar bugs after workspace directory migration: "56 years ago" timestamps, missing threads, threads vanish on click (macOS) #17317

Open

Description

@jjhuang2023

After migrating my workspace from a path containing Chinese characters to an all-English path, the Codex Desktop app exhibits three persistent bugs. The underlying data in state_5.sqlite is fully intact and correct — these are rendering/UI layer issues.

Platform: macOS Darwin 25.4.0 arm64, CLI 0.117.0-alpha.12

Bug 1: All threads display "56 年" (56 years ago) timestamp after restart

After app restart, ALL threads — including newly created ones — show "56 年" (56 years ago) as the relative timestamp. New threads display the correct time at creation, but revert to "56 年" after restart.

This is a Unix epoch (1970) display: 2026 − 1970 = 56 years.

The data is correct — state_5.sqlite timestamps are valid millisecond integers (e.g. 1775804549416 = 2026-04-10), and session_index.jsonl has correct ISO8601 timestamps. The bug is in the app's time rendering on restart.

Bug 2: Some workspaces show "无线程" (no threads) despite having data

The sidebar shows "无线程" for several workspaces, but sqlite has many threads for those exact CWDs:

Workspace Threads in sqlite (archived=0) Sidebar shows
bilibili-video-analysis 14 无线程
health 2 无线程
drafts 30 Only 2 visible
work-management 34 Partial

This appears to be the same truncation/pagination bug reported in #14370.

Bug 3: Threads disappear when clicked

Clicking on a thread in the sidebar causes it to vanish from the list. The thread data still exists in sqlite but is no longer rendered.

Additional: codex app-server continuously modifies sqlite

The codex app-server process (running via Cursor extension) continuously un-archives child threads that have been archived, undoing manual state corrections. It un-archives ~51 child threads within seconds.

Steps to reproduce

  1. Have a workspace with ~247 threads across multiple workspace roots
  2. Restart Codex Desktop app
  3. Observe: all timestamps show "56 年" instead of actual relative time
  4. Observe: some workspace folders show "无线程" despite having threads in sqlite
  5. Click on any thread → it may disappear from the sidebar

Expected behavior

  1. Timestamps should display correct relative time (e.g. "3 days ago")
  2. All workspace folders should list their threads matching sqlite data
  3. Clicking a thread should open it, not hide it

Environment details

  • 247 total threads in state_5.sqlite
  • 141 thread_spawn_edges (parent-child relationships intact)
  • All 247 rollout_path entries point to existing files on disk
  • All timestamps in DB are valid millisecond Unix timestamps
  • WAL has been checkpointed (WAL file is 0 bytes)

Related issues

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    on Apr 10, 2026
  2. Aley3567 commented on Aug 16, 2026

    @Aley3567

    Confirmed independently on macOS with ChatGPT/Codex Desktop 26.810.52044 (6662). This appears to be a mixed timestamp-unit bug and is not specific to a workspace-path migration.

    Evidence

    The thread data exposed to the Desktop renderer contains mixed units:

    • a normal local Codex thread can have updatedAt: 1786849794 (Unix seconds);
    • other entries, including some pinned state, can use a millisecond value such as 1783825615000.

    Passing the first value directly to JavaScript reproduces the symptom:

    new Date(1786849794).toISOString()
    // 1970-01-21T...
    
    new Date(1786849794 * 1000).toISOString()
    // 2026-08-16T...

    In the bundled renderer (webview/assets/app-initial-BqZ9AFkF.js) I found two relevant direct constructions:

    • new Date(q).toISOString() in the hover-card path;
    • new Date(f).toISOString() in the sidebar-row path.

    The same bundle already contains a timestamp normalizer (minified as K8c) that multiplies second-scale values by 1000, but these two paths bypass it. The installed application was not modified; its signature still verifies.

    Suggested fix

    The durable fix should normalize timestamps once at the thread DTO boundary, rather than teaching every component about seconds vs. milliseconds:

    function toEpochMilliseconds(value: unknown): number | null {
      const timestamp = Number(value);
      if (!Number.isFinite(timestamp)) return null;
    
      // Current Unix milliseconds are ~1e12; seconds are ~1e9.
      return Math.abs(timestamp) < 100_000_000_000
        ? timestamp * 1_000
        : timestamp;
    }

    Then keep the renderer's internal contract as epoch milliseconds (or Date) only.

    For a minimal hotfix, route both direct call sites through the existing normalizer:

    new Date(K8c(q)).toISOString()
    new Date(K8c(f)).toISOString()

    Regression coverage should include integer seconds, fractional seconds, milliseconds, missing/invalid values, hover cards, and sidebar rows. That would fix the displayed age without rewriting any persisted timestamps.

  3. Aley3567 commented on Aug 17, 2026

    @Aley3567

    Follow-up: the timestamp bug reproduced again in ChatGPT/Codex Desktop 26.810.52044 (6662).

    I validated the minimal renderer hotfix locally by routing the two bypassing call sites through the bundle's existing seconds/milliseconds normalizer:

    new Date(K8c(q)).toISOString()
    new Date(K8c(f)).toISOString()

    Verification results:

    • both unnormalized call sites are absent after patching;
    • both normalized call sites occur exactly once;
    • ASAR entry count remains unchanged (8779 before and after);
    • the ASAR integrity hash was updated to the actual archive SHA-256;
    • strict deep code-sign verification passes after re-signing;
    • the app's entitlements are byte-for-byte unchanged.

    This confirms that timestamp-unit normalization is sufficient for these two UI paths. The local patch is only a diagnostic/workaround: an app update or repair can replace app.asar, so the durable upstream fix should still normalize updatedAt once at the thread DTO boundary and expose a milliseconds-only internal contract. Component-level normalization is useful as defense in depth, but should not be the primary contract.

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