The Codex Changelog
All Episodes
Fixing Silent Stalls with Codex Queue and Thread Unload Delays

Fixing Silent Stalls with Codex Queue and Thread Unload Delays

0:00|0:00

In Codex 0.166.0, the default thread unload delay drops from thirty minutes to sixty seconds, creating an operational trap where queued commands can sit idle on unloaded threads. Learn how to use the new --resume flag on codex queue to wake dormant threads and avoid silent automation stalls.


Chapter 1

Why queued jobs go quiet

Ethan Park

Hey, it's another day in Codex land, and thanks to Jellypod for helping make this daily show a reality. Last episode we mentioned the new resume flag on codex queue almost in passing. Today we're drilling into why it exists, because it's tied to a default that changed in the same release, 0.166.0, which shipped October 29.

Maya

Start with the old behavior. Codex queue talks to the app server and sends thread/queue/add. That persists your message, and that's all it does. It doesn't wake the thread. GitHub issue 44491 documented the result. Someone queued a message to a saved thread that had unloaded, the command succeeded, and the input sat pending for hours until another client called thread/resume. The maintainer, etraut, closed it as not planned. He said queue intentionally persists input without resuming, and suggested a wrapper script that runs codex queue and then codex resume.

Ethan Park

And here's the part that makes it bite. PR 57242, from jif-oai, cuts the default thread_unload_delay_secs from 1800 seconds to 60. Before, an idle thread with no subscribers stayed in memory for thirty minutes, so queueing from another terminal usually landed on a loaded thread and looked like it worked. Now an idle thread can be unloaded after one minute. The release notes just say reduce the default thread unload delay to one minute. The reading that the server now reclaims resources aggressively and the caller decides whether to wake the thread comes from a third party write up by DataAgent, so treat that part as their interpretation.

Maya

Let me make that concrete. Say a cron job fires every fifteen minutes and queues a message saying run the checks. The thread went idle a few minutes ago, so it's already unloaded. Codex queue exits with success, because the message was accepted. But nothing is executing. In the issue, the reporter's launcher also skipped adding new reminders while the previous one was still pending, so the delays stretched to hours with every exit code green. Success from queue means accepted, not running. That's the operational trap with the new default.

Ethan Park

So CI runners, git hooks, cron, a second terminal pane, anything that pushes work into a thread nobody is watching, is exposed after you upgrade. It's silent, which is the worst kind of change.

Chapter 2

Using the resume flag, and what to watch

Maya

Now the fix. Add the resume flag to codex queue. After queueing, Codex checks the thread status. If the thread is loaded and active, it leaves it alone, and the queued message gets picked up naturally. If the thread reports notLoaded, it calls thread/resume, which wakes it and dispatches the queued submission. A command looks like this: codex queue, thread, your session name or UUID, message, run the test suite and fix lint failures, then the resume flag.

Ethan Park

The thread argument takes a UUID or an exact session name. Per the write up's reading of the source, names only match active sessions, and a miss gives you No active session found matching your name. So name your long running sessions on purpose. For a remote server, add the remote flag with your endpoint, then thread nightly migration, message continue with the next batch, and resume. And if you'd rather have the old lifetime, put thread_unload_delay_secs equals 1800 in your config dot toml. Setting it to zero unloads immediately.

Maya

Caveat one. Queueing and resuming are decoupled. If the message is queued but the resume fails, the command does not fail. It prints Warning, message was queued, but could not resume thread, to stderr, and the message stays persisted. That's reasonable, because your message isn't lost. But in a script, an exit code of zero doesn't prove the wake up worked. Capture stderr and alert on that warning.

Ethan Park

Caveat two, from PR 57273. Codex queue now rejects options it can't honor instead of ignoring them. A model flag, or a config override for model, is rejected, because queued messages run on the thread's existing model. Image attachments are rejected. A prompt passed as a positional argument without the message flag is rejected. The cd flag combined with remote is rejected. And the no daemon flag without remote is rejected, because queue needs a running shared app server. Also, queue appends. It does not interrupt an active turn.

Maya

One thing nobody has documented: when, during a long turn, a queued message actually gets read. The release notes don't say, and the write up flags it as worth testing. Try a harmless task before you wire it into automation.

Ethan Park

A few smaller things in 0.166.0. Safety blocks in the TUI for biosecurity and cybersecurity policies now explain themselves and offer safer rewrites, which fork from the turn before the block with your draft intact. The npm wrapper now hands off to the native binary with execve, so there's one less Node process in the chain and signals behave more directly. Markdown renders consistently in async questions, answers, and transcript replies. And codex doctor with the network only flag checks config, proxy environment, HTTP reachability and the WebSocket handshake, skipping the database and MCP scans.

Maya

Action item for today: grep your scripts for codex queue. Every call that targets a thread nobody is watching either gets the resume flag, or you raise thread_unload_delay_secs. Then watch stderr for that warning.