fix: handle orphan messages in chat history to prevent UI deadlock

When a chat contains orphan messages (missing id, role, or parentId fields)
in history.messages, the UI hangs indefinitely on a loading spinner with no
errors. This happens because:

1. The "load more" check uses strict !== null, so undefined parentId
   (missing field) is treated as "has parent, keep loading" — causing an
   infinite spinner.
2. When currentId points to an orphan, buildMessages() can only reach
   the orphan (no parent chain to valid messages), rendering a blank page.

Fix:
- Use loose != null checks so both undefined and null mean "no parent"
- Add findLastValidMessageId() fallback: when currentId points to an
  invalid message, automatically switch to the most recent valid message
  and render the real conversation

Fixes #15189

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeremy Heng 2026-04-06 22:59:42 +08:00
parent c40ea7f29d
commit eae3d576bb

View file

@ -77,10 +77,37 @@
let pendingRebuild = null;
let lastCurrentId = null;
const findLastValidMessageId = () => {
let lastValidId = null;
for (const [id, msg] of Object.entries(history.messages)) {
if (!msg.id || !msg.role) continue;
if (!lastValidId) {
lastValidId = id;
} else {
const existing = history.messages[lastValidId];
if ((msg.timestamp ?? 0) >= (existing.timestamp ?? 0)) {
lastValidId = id;
}
}
}
return lastValidId;
};
const buildMessages = () => {
let _messages = [];
let message = history.messages[history.currentId];
// If currentId points to an invalid message, fall back to a valid one
if (!message || !message.id || !message.role) {
const fallbackId = findLastValidMessageId();
if (fallbackId) {
console.warn('Invalid currentId, falling back to', fallbackId);
history.currentId = fallbackId;
message = history.messages[fallbackId];
}
}
const visitedMessageIds = new Set();
while (message && (messagesCount !== null ? _messages.length <= messagesCount : true)) {
@ -91,7 +118,7 @@
visitedMessageIds.add(message.id);
_messages.push(message);
message = message.parentId !== null ? history.messages[message.parentId] : null;
message = message.parentId != null ? history.messages[message.parentId] : null;
}
messages = _messages.reverse();
@ -449,7 +476,7 @@
{#key chatId}
<section class="w-full" aria-labelledby="chat-conversation">
<h2 class="sr-only" id="chat-conversation">{$i18n.t('Chat Conversation')}</h2>
{#if messages.at(0)?.parentId !== null}
{#if messages.at(0)?.parentId != null}
<Loader
on:visible={(e) => {
console.log('visible');