@ mention search in thread composer hangs when it searches thread history

Resolved 💬 2 comments Opened Jul 6, 2026 by 8times4 Closed Jul 27, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the Codex App are you using (From “About Codex” dialog)?

26.623.101652

What subscription do you have?

pro 20x

What platform is your computer?

Darwin 24.6.0 arm64 arm

What issue are you seeing?

In the Codex GUI, when I have any thread open and type @ in the message composer, the mention picker appears to search across all historical threads, given that I have a large number of threads, this causes the app to lag or completely hang for 5-10 seconds. (This has improved in the last week or so)

This happens inside an open thread, not from the app-level search UI.

Actual behavior:

  • The app appears to fuzzy-search a large thread history across all threads.
  • The UI becomes unresponsive for several seconds.
  • This makes using @ mentions inside the composer frustrating, especially when I’m trying to reference files.

What steps can reproduce the bug?

Have several threads/subagent threads etc available locally, while inside a chat, type @ then some letter or start typing out some word as if you were searching for a file and the UI will become unresponsive for several seconds.

What is the expected behavior?

  • Typing @ should stay responsive and performant.
  • Files should be surfaced before threads.
  • There should be an option to disable searching threads from the @ picker.

Additional information

this is on an m4max/64gb, so that can't be the issue

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 1 month ago

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

  • #31065

Powered by Codex Action

8times4 · 1 month ago

Turns out, RepoPrompt created threads inside / (can also be elsewhere), with first message starting as <codex reminder> You are operating in text only mode... which effectively named the thread AS the first message (aka up to tens of thousands of tokens), instead of a short title, therefore messing up search in-thread or in-app.

So take-home could be to have a thread title character limit, no matter "who" the originator is

Steps I took:

# Find Codex state DBs
find "$HOME/.codex" -name 'state_*.sqlite' -print

# Count unarchived cwd=/ threads per DB
for db in "$HOME/.codex/state_5.sqlite" "$HOME/.codex/sqlite/state_5.sqlite"; do
  [ -f "$db" ] || continue
  echo "== $db =="
  sqlite3 "$db" "select archived, count(*) from threads where cwd='/' group by archived order by archived;"
done

# Count unarchived <codex reminder> rows, any cwd
for db in "$HOME/.codex/state_5.sqlite" "$HOME/.codex/sqlite/state_5.sqlite"; do
  [ -f "$db" ] || continue
  echo "== $db =="
  sqlite3 "$db" "select cwd, count(*) from threads where archived=0 and (title like '<codex reminder>%' or preview like '<codex reminder>%' or first_user_message like '<codex reminder>%') group by cwd;"
done

# Sample unarchived cwd=/ rows
for db in "$HOME/.codex/state_5.sqlite" "$HOME/.codex/sqlite/state_5.sqlite"; do
  [ -f "$db" ] || continue
  echo "== $db =="
  sqlite3 -header -column "$db" "select id, rollout_path, source, tokens_used, length(title) title_len, substr(title,1,100) title from threads where cwd='/' and archived=0 order by updated_at desc limit 20;"
done

To check whether cwd=/ rows were RepoPrompt-originated:

python3 - <<'PY'
import sqlite3, json
from pathlib import Path

for db in [Path.home()/".codex/state_5.sqlite", Path.home()/".codex/sqlite/state_5.sqlite"]:
    if not db.exists(): continue
    con = sqlite3.connect(db)
    rows = con.execute("select id, rollout_path from threads where cwd='/' and archived=0").fetchall()
    print("\n==", db, "==")
    for thread_id, path in rows[:50]:
        origin = "?"
        try:
            for line in open(path):
                o = json.loads(line)
                if o.get("type") == "session_meta":
                    origin = (o.get("payload") or {}).get("originator")
                    break
        except FileNotFoundError:
            origin = "missing-file"
        print(thread_id, origin, path)
PY

To archive RepoPrompt-originated cwd=/ rows, close Codex first, then:

python3 - <<'PY'
import sqlite3, json, shutil, time
from pathlib import Path

now = int(time.time())
for db in [Path.home()/".codex/state_5.sqlite", Path.home()/".codex/sqlite/state_5.sqlite"]:
    if not db.exists(): continue
    backup = Path(f"/tmp/{db.name}.{db.parent.name}.before-rp-slash-archive.{now}.sqlite")
    shutil.copy2(db, backup)

    con = sqlite3.connect(db)
    rows = con.execute("select id, rollout_path from threads where cwd='/' and archived=0").fetchall()
    ids = []
    for thread_id, path in rows:
        try:
            origin = next(
                ((json.loads(line).get("payload") or {}).get("originator")
                 for line in open(path)
                 if json.loads(line).get("type") == "session_meta"),
                None,
            )
        except FileNotFoundError:
            origin = None
        if origin == "repoprompt":
            ids.append(thread_id)

    con.executemany(
        "update threads set archived=1, archived_at=? where id=?",
        [(now, thread_id) for thread_id in ids],
    )
    con.commit()
    print(db, "archived", len(ids), "backup", backup)
PY