[Bug]: Sidebar "Failed" badge does not clear when the session is opened (unlike "Done")
I maintainer di solito rispondono entro 1 giorno
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 86/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- react, typescript
- Ambito
- frontend
Direzione di ricerca
Leggi apps/web/src/components/sidebar/SidebarThreadRow.tsx intorno alle righe 684–754 e gli helper hasUnseenCompletion e hasUnseenFailure in apps/web/src/components/Sidebar.logic.ts intorno alle righe 689–708. Segui il modo in cui l’apertura di un thread aggiorna lastVisitedAt, quindi controlla i test correlati della barra laterale prima di fare in modo che il badge di errore usi lo stesso comportamento di stato non visto di Done. Done significa che l’apertura di una sessione non riuscita cancella il relativo badge, anche quando completedAt è null.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/web
Summary
In the sidebar thread list, a session card shows a status badge. A "Done" badge clears as soon as you open that session, but a "Failed" badge does not — it stays on the card even after the session has been opened/selected in the sidebar.
The two badges should behave the same way: opening the session should acknowledge (clear) its terminal status badge, whether it finished successfully or failed.
Steps to reproduce
- Have a session run to completion successfully → its sidebar card shows a green Done badge.
- Click/open that session → the Done badge clears.
- Have another session end in failure → its sidebar card shows a red Failed badge (e.g. "Audit private file for public exposure" →
(!) Failed). - Click/open that session.
- Observe: the Failed badge is still on the card.
Expected behavior
Opening a failed session clears (acknowledges) its Failed badge, mirroring how opening a completed session clears the Done badge.
Actual behavior
The Failed badge persists on the sidebar card after the session is opened. Only Done clears on open.
Impact
Minor bug or occasional failure
Root cause (investigated)
The two badges are gated by two independent mechanisms. "Done" is gated by a visited-watermark comparison (so opening clears it); "Failed" is gated by the raw runtime status (which opening never touches).
The card and its two inputs — apps/web/src/components/sidebar/SidebarThreadRow.tsx:684-685
const isUnread = hasUnseenCompletion({ ...thread, lastVisitedAt });
const status = resolveSidebarThreadStatus(thread);
The badge ternary — SidebarThreadRow.tsx:736-754. failed is tested before isUnread, and the two come from different sources:
: status === "failed"
? { label: "Failed", icon: "failed", className: "text-error-foreground" }
: isWoke
? { label: "Woke", ... }
: isUnread
? { label: "Done", icon: "done", className: "text-success-foreground" }
: null;
- Done ⇐
isUnread⇐hasUnseenCompletion(...)— a watermark comparison. - Failed ⇐
status === "failed"⇐resolveSidebarThreadStatus(...)— raw runtime status, no watermark.
Why Done clears on open — hasUnseenCompletion compares against lastVisitedAt (apps/web/src/components/Sidebar.logic.ts:689-698):
export function hasUnseenCompletion(thread: ThreadStatusInput): boolean {
if (!thread.latestRun?.completedAt) return false;
const completedAt = Date.parse(thread.latestRun.completedAt);
if (Number.isNaN(completedAt)) return false;
if (!thread.lastVisitedAt) return false;
const lastVisitedAt = Date.parse(thread.lastVisitedAt);
if (Number.isNaN(lastVisitedAt)) return true;
return completedAt > lastVisitedAt; // false once visited after completion
}
Why Failed does NOT clear on open — resolveSidebarThreadStatus returns "failed" purely from live runtime status, with no lastVisitedAt/seen input (Sidebar.logic.ts:868-895):
if (thread.runtime?.status === "failed") {
return thread.runtime.lastErrorClass === "usage_limit" ? "limited" : "failed";
}
Opening a thread advances lastVisitedAt (local via apps/web/src/components/chat/ThreadVisitWatermark.tsx → markThreadVisited in apps/web/src/uiStateStore.ts:244; server via packages/client-runtime/src/state/threadVisits.ts). That flips hasUnseenCompletion to false (Done clears) but leaves thread.runtime.status === "failed" untouched (Failed stays).
Note: an unused hasUnseenFailure already exists
Sidebar.logic.ts:700-708 already defines a hasUnseenFailure(...) helper, but the active sidebar card (SidebarThreadRow.tsx) does not use it — it uses the raw resolveSidebarThreadStatus status instead. hasUnseenFailure is also asymmetric with hasUnseenCompletion:
function hasUnseenFailure(thread: ThreadStatusInput): boolean {
if (thread.latestRun?.status !== "failed") return false;
if (!thread.lastVisitedAt) return true;
const completedAt = Date.parse(thread.latestRun.completedAt ?? "");
const lastVisitedAt = Date.parse(thread.lastVisitedAt);
if (Number.isNaN(completedAt) || Number.isNaN(lastVisitedAt)) return true; // sticky forever when completedAt is null
return completedAt > lastVisitedAt;
}
A failed run can legitimately have completedAt === null (see packages/client-runtime/src/state/threadSettled.ts:100-108 and packages/client-runtime/src/state/models.ts:260-275, which fall back to runtime.updatedAt). In that case hasUnseenFailure returns true permanently, so even wiring it in as-is would keep the badge sticky for those runs.
Divergence
| Badge | Source | Watermark-gated? | Clears on open? |
|---|---|---|---|
| Done | hasUnseenCompletion |
yes (vs lastVisitedAt) |
✅ |
| Failed | resolveSidebarThreadStatus (runtime.status === "failed") |
no | ❌ |
Suggested fix direction
Gate the Failed badge the same way as Done in SidebarThreadRow.tsx — surface "failed" only when there is an unseen failure (use/wire in hasUnseenFailure(..., lastVisitedAt)), positioned consistently relative to the isUnread check. While doing so, make hasUnseenFailure symmetric with hasUnseenCompletion: fall back to runtime.updatedAt when latestRun.completedAt is null instead of returning true, so failures with no completedAt can still be acknowledged on open.
Relevant files
apps/web/src/components/sidebar/SidebarThreadRow.tsx:684-685, 699-754— active card badgeapps/web/src/components/Sidebar.logic.ts:689-708(hasUnseenCompletion/ unusedhasUnseenFailure),868-895(resolveSidebarThreadStatus)apps/web/src/components/chat/ThreadVisitWatermark.tsx,apps/web/src/uiStateStore.ts:244,packages/client-runtime/src/state/threadVisits.ts— what advanceslastVisitedAton openpackages/client-runtime/src/state/threadSettled.ts:100-108,packages/client-runtime/src/state/models.ts:260-275— failed runs can havecompletedAt === null
Version or commit
main @ 0712398
Environment
apps/web (web app). Observed on macOS.
Contributions welcome
This is open for anyone who wants to pick it up — the root cause and a suggested fix direction are laid out above. If you'd like to take it, leave a comment so we don't double up, and feel free to ask questions on the issue.
- Lingua principale
- TypeScript
- Stelle
- 24.8k
- Fork
- 6.4k
- Merge medio
- 2h 54m
- PR unite (30g)
- 350
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di pingdotgg/t3code
-
[Bug]: Pen taps on sidebar rows start a drag and lose their clickForse già presa @makhnatkin l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
I maintainer di solito rispondono entro 1 giorno
-
[Bug]: Guest messages near the 4,000-char limit fail with 400 "The request was not valid." because multipart turns \n into \r\nForse già presa @juliusmarminge l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 1 giorno
-
[Bug]: Server preview browser is laggy: always launched with --disable-gpu and device scale 2Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
pingdotgg/t3code#26523 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
[Bug]: preview_evaluate forbids WebMCP write tools even when the user explicitly asks for the changeForse già presa Una pull request collegata a questa issue è aperta o già unita. Apertabug needs-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di pingdotgg/t3code
Issue simili
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
scaleway/ultraviolet#7089 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 67/100
tencent-connect/dsh-qqbot#81 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
clangd/vscode-clangd#1009 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
I maintainer di solito rispondono entro 1 giorno