Repository: All-Hands-AI/OpenHands
File: frontend/src/contexts/conversation-websocket-context.tsx (TypeScript)

Code before:
            setIsLoadingHistoryMain(false);
          }
        }

        // Use type guard to validate v1 event structure
        if (isV1Event(event)) {
          addEvent(event);

          // Handle ConversationErrorEvent specifically
          if (isConversationErrorEvent(event)) {
            setErrorMessage(event.detail);
          }

          // Handle AgentErrorEvent specifically
          if (isAgentErrorEvent(event)) {
            setErrorMessage(event.error);

            // Track credit limit reached if the error is budget-related
            if (isBudgetOrCreditError(event.error)) {
              trackCreditLimitReached({
                conversationId: conversationId || "unknown",
              });
            }
          } else if (!isConversationErrorEvent(event)) {
            // Clear error message on any successful (non-error) event
            removeErrorMessage();
          }

          // Clear optimistic user message when a user message is confirmed
          if (isUserMessageEvent(event)) {
            removeOptimisticUserMessage();
          }

          // Handle cache invalidation for ActionEvent
          if (isActionEvent(event)) {
            const currentConversationId =
              conversationId || "test-conversation-id"; // TODO: Get from context
            handleActionEventCacheInvalidation(
              event,
              currentConversationId,
              queryClient,
            );
          }

          // Handle conversation state updates
          // TODO: Tests
          if (isConversationStateUpdateEvent(event)) {
            if (isFullStateConversationStateUpdateEvent(event)) {
              setExecutionStatus(event.value.execution_status);
            }
            if (isAgentStatusConversationStateUpdateEvent(event)) {

Review comment (suggestion):
**question:** Have you verified this work locally? Could you also attach a short demo video? Additionally, once the ConversationErrorEvent and other events (such as stats or conversation status updates) are sent, will the error message be cleared immediately—even if I haven’t seen the error message yet to be aware of the issue? Thank you! 🙏 

Code after:
            setIsLoadingHistoryMain(false);
          }
        }

        // Use type guard to validate v1 event structure
        if (isV1Event(event)) {
          addEvent(event);

          // Handle ConversationErrorEvent specifically - show error banner
          // AgentErrorEvent errors are displayed inline in the chat, not as banners
          if (isConversationErrorEvent(event)) {
            setErrorMessage(event.detail);
          } else {
            // Clear error message on any non-ConversationErrorEvent
            removeErrorMessage();
          }

          // Track credit limit reached if AgentErrorEvent has budget-related error
          if (isAgentErrorEvent(event)) {
            if (isBudgetOrCreditError(event.error)) {
              trackCreditLimitReached({
                conversationId: conversationId || "unknown",
              });
            }
          }

          // Clear optimistic user message when a user message is confirmed
          if (isUserMessageEvent(event)) {
            removeOptimisticUserMessage();
          }

          // Handle cache invalidation for ActionEvent
          if (isActionEvent(event)) {
            const currentConversationId =
              conversationId || "test-conversation-id"; // TODO: Get from context
            handleActionEventCacheInvalidation(
              event,
              currentConversationId,
              queryClient,
            );
          }

          // Handle conversation state updates
          // TODO: Tests
          if (isConversationStateUpdateEvent(event)) {
            if (isFullStateConversationStateUpdateEvent(event)) {
              setExecutionStatus(event.value.execution_status);
            }
            if (isAgentStatusConversationStateUpdateEvent(event)) {
              setExecutionStatus(event.value);
            }