[Windows] Desktop startup MoAppHang from unbounded duplicated thread metadata after automation creates large first-user prompts

Open 💬 2 comments Opened Aug 23, 2026 by wizardpc-com
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

Codex Desktop on Windows became effectively unable to start after a long-running local automation workflow created many Codex threads whose first user messages grew into hundreds of thousands of characters.

The local SQLite databases were structurally healthy. The evidence strongly implicates valid but extremely oversized thread metadata as the cause:

  • title
  • preview
  • first_user_message

For many affected rows, all three fields contained the same full first-user prompt.

The resulting state_5.sqlite grew to roughly 524 MB, with the threads table accounting for about 519 MB. Codex Desktop then repeatedly hung during startup with Windows Event ID 1002 / WER MoAppHang.

Renaming the existing %USERPROFILE%\.codex and allowing Desktop to create a fresh profile immediately restored startup, strongly isolating the failure to local Codex state.

This appears closely related to #21154, #21211, #29007, #32371, #32588, and #35604, but this report adds a reproducible Windows case, a concrete automation trigger, detailed measurements, and a validated recovery path.

---

Environment

Observed affected environment:

  • Windows x64
  • Codex Desktop package: 26.818.5229.0
  • Desktop executable/runtime version reported by WER: 151.0.7922.170
  • Failure type: MoAppHang
  • Windows Event ID: 1002

The issue appeared abruptly after the same installation had worked normally earlier.

Repair/reset of the Windows app did not resolve the original state-dependent startup hang.

---

User-visible behavior

Typical sequence:

  1. Start Codex Desktop.
  2. Initial UI appears.
  3. Within seconds the window stops responding.
  4. Windows reports the application as hung.
  5. Repeated launches behave the same way.
  6. Occasionally the app remains usable for only a few seconds before freezing.

Windows Error Reporting repeatedly recorded:

EventName: MoAppHang
Application: OpenAI.Codex
Event ID: 1002

There was no evidence that this was simply a network-loading failure.

---

Isolation test

A reversible A/B test isolated the problem to the existing Codex profile:

%USERPROFILE%\.codex

was renamed and Desktop was allowed to create a fresh profile.

Result:

  • old profile: reproducible startup hang
  • fresh profile: Desktop started normally and reached login

No existing data was deleted.

This excluded the installation itself as the primary cause.

---

Database integrity

Read-only inspection of the affected profile found no physical SQLite corruption.

Checks included:

PRAGMA integrity_check / quick_check: OK
foreign-key checks: OK
JSON / JSONL parsing: OK
WAL structure: no truncation evidence

All mapped session JSONL files were syntactically valid.

This was therefore a valid-data / pathological-metadata failure, not database corruption.

---

Size of the affected local state

Affected profile:

threads:             1,218
active threads:        676
archived threads:      542

state_5.sqlite:      ~524 MB
threads table:       ~519 MB

logs_2.sqlite:       ~1.0 GB
sessions:            ~2.5 GB

The log database was large and may have amplified I/O cost, but evidence did not identify it as the source of the failure.

---

Oversized metadata

The strongest signal was the threads table.

Across the 1,218 rows:

sum(length(title)):              ~147.6 million characters
sum(length(first_user_message)): ~147.6 million characters
sum(length(preview)):            ~147.6 million characters

For 1,179 threads, the three fields were identical:

title == first_user_message == preview

Distribution by first-user-message size:

| First user message | Threads |
|---|---:|
| <= 10 KiB | 254 |
| 10-64 KiB | 526 |
| 64-256 KiB | 224 |
| > 256 KiB | 214 |

Additional observations:

  • 438 threads had first messages larger than 64 KiB.
  • 214 exceeded 256 KiB.
  • The largest affected metadata field was approximately 660,000 characters.
  • For a single extreme thread, that same text was effectively stored three times as list/display metadata.

The large values were not random or corrupted text.

For almost all affected rows, first_user_message matched the actual first event_msg/user_message in the session JSONL.

---

Trigger in this incident

The main producer was a custom local automation workflow using the Codex app-server.

Its controller loop repeatedly created new threads.

A bug in that workflow caused a growing state snapshot / transcript to be embedded directly into the first user message of every new controller thread.

The approximate chain was:

autonomous controller
    -> growing state snapshot / transcript
    -> new controller thread
    -> snapshot inserted inline as first user prompt
    -> Codex indexes the complete prompt
    -> title = first_user_message = preview
    -> threads table grows rapidly
    -> startup/thread-list path becomes pathological
    -> Windows Desktop MoAppHang

The problem became much worse during two automation runs that generated roughly 150 new controller threads.

Their first-user prompts grew to approximately:

530,000 - 661,000 characters

New controller sessions were being created roughly every 1-2 minutes.

This custom automation was clearly defective because it allowed state to grow without a bound.

However, Codex Desktop also appears insufficiently defensive because a valid large prompt can become unbounded UI/list metadata and be duplicated across multiple hot-path fields.

---

Why this appears to be a Codex robustness issue too

The automation should not have generated these prompts, but a local client should ideally remain recoverable when it encounters them.

The problematic invariant was:

unbounded conversation content
        ->
unbounded display/list metadata
        ->
duplicated across title / preview / first_user_message
        ->
appears to reach / be materialized by thread-list / startup / navigation paths

A single large prompt should not be able to poison the entire Desktop profile.

This is consistent with the broader failure mode already discussed in related issues such as #29007 and #32371.

There is also evidence that this class is not unique to the custom automation.

After recovery, unrelated internal review/subagent threads still produced metadata in the approximately 46-54 KiB range, although this was far below the size required to reproduce the incident.

That appears related to #32588.

---

User-side mitigation applied

The autonomous workflow was changed so that complete history is no longer inserted into the initial controller prompt.

New design:

full context/history
    -> external append-only files / archives

new controller first prompt
    -> run ID
    -> current task
    -> bounded state summary
    -> paths/references to full context

Hard limits were added:

controller first prompt:
    target < 4 KiB
    hard reject at >= 10 KiB

state snapshot:
    hard reject at >= 128 KiB

Raw transcripts, previous snapshots, and tool outputs are no longer recursively embedded.

The system fails closed if a controller prompt exceeds the limit.

Regression tests also verify that prompt size does not increase with accumulated history.

---

Post-fix real-world canary

A real app-server canary was run through the same automation path.

Observed:

maximum controller prompt:       2,910 bytes
consecutive controller prompts:  2,910 -> 2,910 bytes
growth:                          0 bytes

maximum state snapshot:          79,460 bytes

No transcript or raw-output content appeared inline.

New automation-generated thread metadata remained approximately 2.9 KiB.

After the test:

SQLite quick_check: OK
Event ID 1002:       0
MoAppHang:           0
Desktop restart:     successful

This strongly supports the identified trigger chain.

---

Recovery observations

I did not restore the affected state_5.sqlite.

Instead, I created a fresh profile and restored persistent configuration separately from conversation history.

Configuration/workspace recovery deliberately excluded:

state_5.sqlite*
logs_2.sqlite*
thread_history databases
session_index
WAL / SHM
caches
temporary state
old plugin caches

Only selected user conversations were later restored from original JSONL session files.

Important app-server behavior observed during recovery

A copied legacy rollout JSONL was initially not obvious in the Desktop list.

However:

  1. thread/read(threadId) successfully read the restored rollout.
  2. Codex subsequently had a valid row for the thread in state_5.sqlite.
  3. Exact thread/list queries using:
  • searchTerm
  • sourceKinds
  • cwd
  • pagination/cursor
  • useStateDbOnly=true

successfully found the thread.

  1. Desktop could then navigate to and open it.
  2. thread/resume was not required.
  3. No new turn was created.

A previous thread/list(limit=50) check had incorrectly suggested that the thread was invisible simply because it was not on the first result page.

This may be useful when diagnosing apparently missing legacy threads.

---

Final recovery validation

A selected set of recent user threads was restored after the automation fix.

Final result:

threads restored:       28
JSONL files restored:   29
SHA-256 matches:        29 / 29
temporary files:        0

No old SQLite database, index, or thread mapping was copied.

Post-recovery:

state_5.sqlite quick_check: OK
foreign-key errors:          0
restored thread rows:        28 / 28
maximum restored metadata:   ~23 KiB
Event ID 1002:               0
Desktop representative open tests: PASS

The Desktop remained responsive.

---

Recovery-tool mistakes that were NOT Codex bugs

For clarity, two failures encountered while building the local recovery tooling were caused by my recovery scripts rather than Codex:

1. Bad file descriptor

The initial recovery helper used an incorrect file-descriptor lifecycle around temporary-file / flush / fsync handling on Windows.

It failed before committing the first restored session.

The transaction was fail-closed and left the Codex profile unchanged.

2. Overly strict "entire .codex must be static" assumption

The first helper assumed the entire profile must remain byte-for-byte static while validating a restore.

This was invalid when other Codex processes were active.

The recovery logic was changed to verify only the target file, target thread row, and attributable metadata.

These are included here only to distinguish local recovery-tool errors from the Codex failure itself.

---

Expected behavior

I would expect Codex to enforce an invariant similar to:

full conversation content belongs in rollout/session history

thread-list/display metadata must always remain bounded

In particular:

  1. title should have a strict maximum display length.
  2. preview should have a strict maximum preview length.
  3. first_user_message should either:
  • be bounded, or
  • not be selected/materialized on startup/list hot paths.
  1. A large valid prompt should not be duplicated unboundedly into all three fields.
  2. Startup/thread reconciliation should stream/page rows rather than eagerly materializing an arbitrarily large metadata table.
  3. Existing poisoned profiles should degrade gracefully instead of making Desktop unusable.

---

Suggested fixes

Metadata generation

Apply hard bounds when generating/upserting thread metadata.

For example, conceptually:

title:              short display label
preview:            bounded preview
first_user_message: bounded projection

The raw rollout JSONL should remain the source of truth for complete content.

Thread-list/startup paths

Avoid eager loading of all large text fields.

Prefer:

  • paging/keyset iteration;
  • selecting only fields actually required by the current UI;
  • lazy retrieval of full content;
  • bounded projections.

Repair path

A supported local repair/doctor workflow would be valuable.

For example, detect:

oversized thread metadata
metadata DB unexpectedly large
title == preview == full first prompt

and offer to rebuild bounded metadata from session history without deleting the underlying conversations.

Internal automatic threads

Completed internal review/subagent/automation threads should ideally either:

  • use bounded metadata;
  • be excluded from normal user thread-list hot paths; or
  • be automatically archived when their lifecycle ends.

---

Minimal reproduction

A simplified reproduction should not require the original automation system.

Conceptually:

  1. Start a Codex thread through an app-server/CLI/VS Code-origin path.
  2. Make the first user message several hundred KiB long.
  3. Do not assign an explicit short title.
  4. Repeat enough times to produce many such threads.
  5. Inspect state_5.sqlite.
  6. Check whether:
title == first_user_message == preview

with each field containing the full prompt.

  1. Restart Codex Desktop and observe thread-list/startup performance.

I have not intentionally reproduced the full hang again after recovery because I did not want to re-poison a healthy profile.

---

Privacy / artifacts

I am intentionally not attaching:

  • the original SQLite databases;
  • raw session JSONL;
  • logs containing conversation content;
  • local workspace paths;
  • thread IDs;
  • authentication/plugin secrets;
  • raw prompts.

Those artifacts contain private local information.

I can provide additional sanitized aggregate statistics, schemas, or narrowly redacted diagnostics if maintainers need them.

---

Related issues

Likely related:

  • #21154
  • #21211
  • #24510
  • #29007
  • #32371
  • #32588
  • #35604

This may ultimately be a duplicate of the same underlying unbounded-metadata family, but I wanted to provide the Windows MoAppHang reproduction, the automation trigger chain, and the validated recovery experience.

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 4 days ago

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

  • #39966

Powered by Codex Action

wizardpc-com · 3 days ago

I took a closer look at #39966. I think the two issues are related, but they don't look like the same failure mode.

#39966 is mainly about high subagent concurrency together with OAuth/re-authentication loops, renderer hangs, and black-screen/GPU failures.

What I saw in #40255 was different:

  • no OAuth loop or black screen;
  • repeated MoAppHang / Event ID 1002 during Desktop startup;
  • state_5.sqlite grew to ~524 MB, with the threads table alone at ~519 MB;
  • 1,179 threads had title == preview == first_user_message;
  • hundreds of those fields contained 64K–660K-character first-user prompts;
  • those oversized values could be traced back to valid prompts created by a local automation workflow, rather than database corruption.

After bounding new automation-generated first prompts to ~2.9 KiB and rebuilding from a clean local state, Desktop started normally again and I saw no new Event ID 1002 / MoAppHang.

So there may be a broader shared problem around Desktop robustness with large local state, but #40255 seems to be a more specific case involving unbounded duplicated thread metadata in state_5.sqlite.