diff --git a/src/stores/WorkspaceStore.ts b/src/stores/WorkspaceStore.ts index ec649630b..99a368928 100644 --- a/src/stores/WorkspaceStore.ts +++ b/src/stores/WorkspaceStore.ts @@ -964,13 +964,24 @@ export class WorkspaceStore { const historicalMsgs = this.historicalMessages.get(workspaceId) ?? []; historicalMsgs.push(data); this.historicalMessages.set(workspaceId, historicalMsgs); - } else if (isCaughtUp) { + } else if (isCaughtUp && "role" in data) { // Process live events immediately (after history loaded) + // Check for role field to ensure this is a CmuxMessage aggregator.handleMessage(data); this.states.bump(workspaceId); this.checkAndBumpRecencyIfChanged(); + } else if ("role" in data || "type" in data) { + // Unexpected: message with role/type field didn't match any condition + console.error("[WorkspaceStore] Message not processed - unexpected state", { + workspaceId, + isCaughtUp, + hasRole: "role" in data, + hasType: "type" in data, + type: "type" in data ? (data as { type: string }).type : undefined, + role: "role" in data ? (data as { role: string }).role : undefined, + }); } - // Note: Init events and stream events are handled by isStreamEvent() buffering above + // Note: Messages without role/type are silently ignored (expected for some IPC events) } } diff --git a/src/utils/messages/StreamingMessageAggregator.ts b/src/utils/messages/StreamingMessageAggregator.ts index 67af75874..d8ab95b05 100644 --- a/src/utils/messages/StreamingMessageAggregator.ts +++ b/src/utils/messages/StreamingMessageAggregator.ts @@ -511,15 +511,30 @@ export class StreamingMessageAggregator { } if (isInitOutput(data)) { - if (!this.initState) return; // Defensive: shouldn't happen but handle gracefully + if (!this.initState) { + console.error("Received init-output without init-start", { data }); + return; + } + if (!data.line) { + console.error("Received init-output with missing line field", { data }); + return; + } const line = data.isError ? `ERROR: ${data.line}` : data.line; + // Extra defensive check (should never hit due to check above, but prevents crash if data changes) + if (typeof line !== "string") { + console.error("Init-output line is not a string", { line, data }); + return; + } this.initState.lines.push(line.trimEnd()); this.invalidateCache(); return; } if (isInitEnd(data)) { - if (!this.initState) return; // Defensive: shouldn't happen but handle gracefully + if (!this.initState) { + console.error("Received init-end without init-start", { data }); + return; + } this.initState.exitCode = data.exitCode; this.initState.status = data.exitCode === 0 ? "success" : "error"; this.invalidateCache();