Repository navigation
Description
Summary
The Claude Code Codex plugin (codex@openai-codex v1.0.5) fails every fresh delegated task with:
no rollout found for thread id <uuid>
Root cause is an ordering bug against current app-server semantics: the plugin calls thread/name/set immediately after thread/start, before the first turn runs. In codex-cli 0.142.5 a non-ephemeral thread's rollout file is created lazily at the first turn/start, not at thread/start. thread/name/set is a disk/thread-store path that requires the rollout to already exist, so it returns -32600 no rollout found for thread id …. The plugin's startThread() catch only swallows unknown variant / unknown method, so it rethrows this error and the whole task fails.
Because persistThread: true (which sets a thread name) is used for every task run, this is deterministic — the plugin's task path is effectively unusable on this CLI version, forcing users to fall back to codex exec.
Environment
- codex-cli 0.142.5 (Homebrew cask), macOS (Darwin arm64)
- Claude Code Codex plugin 1.0.5 (
codex@openai-codex, gitCommitSha80c31f9) - Transport:
codex app-serverover newline-delimited JSON-RPC 2.0
Where it happens (plugin source)
scripts/lib/codex.mjs — startThread():
async function startThread(client, cwd, options = {}) {
const response = await client.request("thread/start", buildThreadParams(cwd, options));
const threadId = response.thread.id;
if (options.threadName) {
try {
await client.request("thread/name/set", { threadId, name: options.threadName });
} catch (err) {
// Only suppresses "unknown variant/method" from older CLIs.
const msg = String(err?.message ?? err ?? "");
if (!msg.includes("unknown variant") && !msg.includes("unknown method")) {
throw err; // <-- "no rollout found" is rethrown here → task fails
}
}
}
return response;
}runAppServerTurn()starts a non-ephemeral thread for tasks (ephemeral: options.persistThread ? false : true) and always passes athreadName, then runsturn/startafterstartThread()returns.executeTaskRun()(scripts/codex-companion.mjs) always calls withpersistThread: trueand athreadName, sothread/name/setis always attempted before any turn.
app-server side (matches source at tag rust-v0.142.5): a plain thread/start inserts the thread into the in-memory map but writes no rollout to $CODEX_HOME/sessions/; the rollout + state-db row are created when the first turn begins. thread/name/set resolves the rollout from disk (resolve_rollout_path → no rollout found when absent), unlike turn/start, which uses the in-memory handle (and would report thread not found on a miss, not no rollout found).
Minimal reproduction (no plugin needed)
// repro.mjs — run: node repro.mjs (from any dir, with codex 0.142.5 on PATH)
import { spawn } from "node:child_process";
import readline from "node:readline";
const p = spawn("codex", ["app-server"], { stdio: ["pipe", "pipe", "pipe"] });
p.stdout.setEncoding("utf8");
const pending = new Map(); let id = 0;
readline.createInterface({ input: p.stdout }).on("line", (l) => {
if (!l.trim()) return; let m; try { m = JSON.parse(l); } catch { return; }
if (m.id !== undefined && (m.result !== undefined || m.error !== undefined)) {
const r = pending.get(m.id); if (r) { pending.delete(m.id); m.error ? r.reject(m.error) : r.resolve(m.result); }
}
});
const req = (method, params) => new Promise((res, rej) => { const i = ++id; pending.set(i, { resolve: res, reject: rej }); p.stdin.write(JSON.stringify({ id: i, method, params }) + "\n"); });
const notify = (method, params) => p.stdin.write(JSON.stringify({ method, params }) + "\n");
await req("initialize", { clientInfo: { name: "repro", version: "0", title: "repro" }, capabilities: {} });
notify("initialized", {});
const st = await req("thread/start", { cwd: process.cwd(), model: null, approvalPolicy: "never", sandbox: "read-only", serviceName: "claude_code_codex_plugin", ephemeral: false });
const tid = st.thread.id;
try {
await req("thread/name/set", { threadId: tid, name: "My Task" }); // BEFORE any turn — same as the plugin
console.log("name/set OK");
} catch (e) {
console.log("name/set FAILED:", JSON.stringify(e)); // -> {"code":-32600,"message":"no rollout found for thread id <tid>"}
}
p.kill("SIGTERM");Observed: name/set FAILED: {"code":-32600,"message":"no rollout found for thread id …"} — 10/10 runs, and still fails even with a 2000 ms delay before name/set (the rollout is genuinely not written until the first turn, not merely a flush race). If you instead run turn/start first and only then thread/name/set, the no rollout found error disappears.
Expected vs actual
- Expected: delegating a fresh task through the Claude Code plugin runs the turn; naming the thread is best-effort and never fails the task.
- Actual: the task fails immediately with
no rollout found for thread id …before the turn ever runs.
Suggested fix (plugin side)
Make thread naming non-fatal / correctly ordered. Any of:
- Treat
thread/name/setfailures as best-effort — extend thestartThread()catch to also swallowno rollout found(and the post-turnthread metadata unavailable before name updatevariant). Naming is cosmetic here:--resume-lastresolves the thread from the plugin's own trackedjob.threadId(resolveLatestTrackedTaskThread→findLatestResumableTaskJob), not the codex-side thread name. - Reorder — apply
thread/name/setafter the first turn has produced a rollout (aligns with the desired behavior in Auto-generate and apply a concise thread name after the first prompt #24289).
Option 1 is the smallest and most robust across CLI versions.
Related
- app-server: JSON-RPC
-32600is overloaded across "no rollout found" and "failed to load configuration" — please make the two discriminable #28496 —-32600overloaded acrossno rollout foundand config-load errors (same error surface; recommends fall-back-to-thread/startfor the resume half). - Auto-generate and apply a concise thread name after the first prompt #24289 — auto-name thread after the first prompt (confirms the correct ordering).
- codex exec resume <missing-uuid> surfaces noisy "thread <uuid> not found"; fall back to thread/start instead #22064 — resume of a missing thread should fall back to
thread/start.
Activity
github-actions commentedon Jul 5, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
T2hhbmEK commentedon Jul 5, 2026
Not a duplicate of #31004.
#31004 is the Codex Desktop app on Windows becoming broadly unusable (new chats time out, "Loading…" never resolves) — and that reporter says the CLI works.
This (#31158) is the Claude Code Codex plugin on
codex app-server(macOS): a deterministic, network/account-independent ordering bug with a ~25-line repro —thread/name/setis sent right afterthread/start, but a non-ephemeral thread's rollout is only created at the firstturn/start, so the name-set disk read fails withno rollout foundand the plugin rethrows it.Only the error string overlaps, and #28496 notes that string is overloaded across unrelated cases. Happy to link as related, not duplicate.
lsdlsd88 commentedon Nov 9, 2026
still an issue??
Error starting chat
no rollout found for thread id 01a1bf79-0259-74e7-8725-ba176a7e7045
any workaroud