pingdotgg / pingdotgg/t3code

[Bug]: New thread in a freshly-created project gets stuck on "Loading messages..." forever, even though the backend completes the turn

Open
#11,681 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
23k
Forks
5.9k
Avg merge
11h 14m
Merged PRs (30d)
357

Description

Title: [Bug]: New thread in a freshly-created project gets stuck on "Loading messages..." forever, even though the backend completes the turn

Area: apps/desktop

Steps to reproduce:

  1. Point T3 Code at a project folder whose path recently changed on disk (e.g. moved from ~/Desktop/Projects/foo to ~/Projects/foo), such that the old sidebar entry no longer resolves cleanly.
  2. Open/select that project and send the very first message in a brand-new thread.
  3. Observe: T3 silently creates a second, duplicate project record for the same folder (confirmed via ~/.t3/userdata/state.sqlite, projection_projects — two rows with the same title and different workspace_root casing/path, one created seconds before the new thread).
  4. The new thread's turn actually completes successfully server-side within ~10 seconds (all orchestration_events for the thread — thread.createdthread.message-sentthread.turn-start-requested → several thread.session-setthread.message-sent (assistant) → thread.activity-appended → final thread.session-set (status ready) → thread.meta-updated — fire and complete cleanly, no errors).
  5. Despite this, the composer UI stays on "Loading messages..." indefinitely. The main content pane never renders anything (not even the two messages that exist in projection_thread_messages).

Expected behavior: The thread should render the completed turn's messages and clear the loading state, exactly as it does in every other (non-brand-new) project's threads.

Actual behavior: The composer shows "Loading messages..." forever. Typing and clicking send is a silent no-op (text stays in the box, no new turn starts, sidebar timestamp never updates). Switching to model Sonnet+Low, navigating away to a different thread and back, and reloading did not clear it. state.sqlite shows the turn is state=completed, the thread session status=ready, last_error is null, and both the user and assistant messages exist in full with is_streaming=0. So this is purely a client-side rendering/subscription bug, not a backend failure — my best guess is a race where the renderer's live-event subscription for the thread attaches after the whole event burst (all 16 events within ~12s) has already fired, so it's left waiting on a "loaded" signal that already happened and never arrives again.

Impact: Blocks work completely (on that one thread — have to abandon it and start a new thread, which works fine).

Version: 0.0.40 (macOS, Alpha)

Environment: macOS 25.5.0 (Darwin), T3 Code (Alpha) 0.0.40

Workaround: None found that recovers the stuck thread (tried: switching model, switching threads and back, retyping and resending). Only workaround is to delete/archive the stuck thread and start a new one in the same project, which loads normally.

Secondary bug also observed: T3 silently created a duplicate project entry for the same on-disk folder (reachable via two path spellings due to a symlink, ~/Desktop/Projects/foo -> ~/Projects/foo) instead of resolving to the existing project record. This orphaned the new thread in a project with zero prior history, likely contributing to the race above (a project with no established event history has its very first thread hit the full burst-of-events-before-subscription race described above).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in apps/desktop and reproduce with a project whose path recently changed, then inspect the thread loading and live-event subscription behavior. Compare the UI state with state.sqlite, especially projection_projects, projection_thread_messages, and orchestration_events. Done means a first thread in the affected project renders its completed messages, clears "Loading messages...", and does not create a duplicate project record.

Written by the indexing model from the issue text.

Assessment

Tech stack
sqlite, typescript
Domain
databases, desktop, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.