Skip to content

fix(pearl): correct SSE error event format and fix infinite loading o…#357

Open
HARSH048 wants to merge 1 commit intobubblelabai:mainfrom
HARSH048:fix/sse-error-event-format-and-infinite-loading
Open

fix(pearl): correct SSE error event format and fix infinite loading o…#357
HARSH048 wants to merge 1 commit intobubblelabai:mainfrom
HARSH048:fix/sse-error-event-format-and-infinite-loading

Conversation

@HARSH048
Copy link
Copy Markdown

@HARSH048 HARSH048 commented Apr 19, 2026

…n missing API key

Three related bugs fixed:

  1. Wrong SSE error event shape in backend routes

    • bubble-flows.ts and ai.ts were sending { type: 'error', error: '...' }
    • StreamingEvent schema requires { type: 'error', data: { error, recoverable } }
    • Frontend handler reads event.data.error, so errors silently fell back to 'An error occurred' or were swallowed entirely
  2. Missing error event on coffee agent failure (planning phase)

    • When GOOGLE_API_KEY is absent, coffee.ts returns early with success:false
    • The route only sent stream_complete with the error buried in coffeeResult
    • No error SSE event was emitted, so the frontend never received it
  3. UI stuck in infinite loading state when API key is missing

    • generationAbortController was never cleared after the stream ended
    • isGenerating stayed true forever since setGenerationCompleted was never called
    • Added stream_complete error handling in handleStreamingEvent as a fallback
    • Added safety net in startGenerationStream to always clear isGenerating after the stream loop exits without a generation_complete event

Summary

Related Issues

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactor
  • New Bubble Integration
  • Other (please describe):

Checklist

  • My code follows the code style of this project
  • I have added appropriate tests for my changes
  • I have run pnpm check and all tests pass
  • I have tested my changes locally
  • I have linked relevant issues

Screenshots (Required)

For New Bubble Integrations

📋 Integration Flow Tests: When creating a new bubble, you must write an integration flow test that exercises all operations end-to-end in realistic scenarios—including edge cases. This flow should be runnable in bubble studio and return structured results tracking each operation's success/failure with details. See packages/bubble-core/src/bubbles/service-bubble/google-sheets/google-sheets.integration.flow.ts for a complete reference implementation.

⚠️ If your integration requires API credits for testing, please reach out to the team for test credentials.

  • Integration flow test (.integration.flow.ts) covers all operations
  • Screenshots showing full test results in Bubble Studio attached above

Additional Context

Summary by CodeRabbit

  • Bug Fixes
    • Ensure error events are consistently reported with structured payloads so clients receive clear error details.
    • Emit error notifications before stream completion so the UI sees errors prior to finalization.
    • Prevent stuck loading states by clearing generation state when a stream finishes without a completion event.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c243da7-40f0-4f3e-8fa0-af532f711421

📥 Commits

Reviewing files that changed from the base of the PR and between ae5de08 and 2f9a0e9.

📒 Files selected for processing (4)
  • apps/bubble-studio/src/hooks/usePearlChatStore.ts
  • apps/bubble-studio/src/hooks/usePearlStream.ts
  • apps/bubblelab-api/src/routes/ai.ts
  • apps/bubblelab-api/src/routes/bubble-flows.ts
💤 Files with no reviewable changes (1)
  • apps/bubble-studio/src/hooks/usePearlChatStore.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/bubblelab-api/src/routes/ai.ts
  • apps/bubblelab-api/src/routes/bubble-flows.ts

📝 Walkthrough

Walkthrough

Adjusted streaming SSE behavior and payload shape: removed an ignored-event comment, added a frontend safety cleanup to cancel unfinished generation, and unified backend SSE error event payloads to nest { error, recoverable } under data, plus emit error before stream completion in a planning failure.

Changes

Cohort / File(s) Summary
Frontend streaming logic
apps/bubble-studio/src/hooks/usePearlStream.ts, apps/bubble-studio/src/hooks/usePearlChatStore.ts
Added a post-stream safety check in startGenerationStream to call cancelGenerationStream() if generationCompleted remained false; removed an inline comment in handleStreamingEvent for control events (no behavioral change).
Backend SSE error payloads & ordering
apps/bubblelab-api/src/routes/ai.ts, apps/bubblelab-api/src/routes/bubble-flows.ts
Restructured SSE error event payloads to emit { type: 'error', data: { error, recoverable } }; ensured planning failures emit an error SSE before stream_complete so the client receives the error prior to completion.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • iqbalbhatti49
  • zhubzy
  • Selinali01

Poem

🐇 I hopped through streams both near and far,
Tidied signals and shaped the error star.
A safety hop to end the load,
Payloads tucked in a neat small code,
Hooray — no more stuck UI mar!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main changes: fixing SSE error event format and resolving an infinite loading issue when API key is missing.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/bubblelab-api/src/routes/bubble-flows.ts (1)

1421-1446: ⚠️ Potential issue | 🟡 Minor

Avoid emitting two error signals for the same failure.

When coffeeResult.success === false, the backend now sends an error SSE event and then a stream_complete event that still carries the failed coffeeResult. On the frontend, both case 'error' (lines 410-422 in usePearlChatStore.ts) and case 'stream_complete' (lines 429-445, newly added) independently call state.addEvent({ type: 'generation_error', ... }) and state.setGenerationCompleted(true). This will duplicate the error in the timeline UI.

Pick one source of truth — either stop including the failing coffeeResult in stream_complete after emitting the error event, or drop the coffeeResult branch from the frontend stream_complete handler now that error is properly emitted.

🔧 Proposed server-side fix
           if (!coffeeResult.success) {
             await stream.writeSSE({
               data: JSON.stringify({
                 type: 'error',
                 data: {
                   error:
                     coffeeResult.error ||
                     'Flow generation failed. Please check your API key configuration.',
                   recoverable: false,
                 },
               }),
               event: 'error',
             });
           }

           await stream.writeSSE({
             data: JSON.stringify({
               type: 'stream_complete',
               timestamp: new Date().toISOString(),
-              coffeeResult,
+              ...(coffeeResult.success ? { coffeeResult } : {}),
             }),
             event: 'stream_complete',
           });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/bubblelab-api/src/routes/bubble-flows.ts` around lines 1421 - 1446, The
backend is emitting both an 'error' SSE and then a 'stream_complete' SSE that
still includes the failing coffeeResult, causing duplicate error handling
client-side; update the code around the coffeeResult handling in the
bubble-flows route so that when coffeeResult.success === false you still emit
the 'error' SSE via stream.writeSSE(...) but send the subsequent
'stream_complete' SSE without the failing coffeeResult (e.g., omit the
coffeeResult field or set it null/undefined) so the frontend's stream_complete
handler no longer treats it as an additional error; ensure the 'error' branch
uses the existing event payload and only the successful path includes
coffeeResult in the stream_complete payload.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/bubble-studio/src/hooks/usePearlChatStore.ts`:
- Around line 429-445: The 'stream_complete' branch currently duplicates
handling already done in the 'error' SSE handler and blindly casts event to read
coffeeResult; remove the coffeeResult failure branch inside the 'case
"stream_complete"' or guard it so it only runs when no prior 'error' event was
received (e.g., track a local flag like errorEventReceived set by the existing
'case "error"' handler before calling
state.addEvent/state.setGenerationCompleted). Also avoid the blind cast by using
the shared StreamingEvent type (or extend that schema to include coffeeResult)
when reading coffeeResult to keep typings correct.

---

Outside diff comments:
In `@apps/bubblelab-api/src/routes/bubble-flows.ts`:
- Around line 1421-1446: The backend is emitting both an 'error' SSE and then a
'stream_complete' SSE that still includes the failing coffeeResult, causing
duplicate error handling client-side; update the code around the coffeeResult
handling in the bubble-flows route so that when coffeeResult.success === false
you still emit the 'error' SSE via stream.writeSSE(...) but send the subsequent
'stream_complete' SSE without the failing coffeeResult (e.g., omit the
coffeeResult field or set it null/undefined) so the frontend's stream_complete
handler no longer treats it as an additional error; ensure the 'error' branch
uses the existing event payload and only the successful path includes
coffeeResult in the stream_complete payload.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2ecf1312-e7bb-49b3-be69-e87e27f092d1

📥 Commits

Reviewing files that changed from the base of the PR and between 06974f8 and ae5de08.

📒 Files selected for processing (4)
  • apps/bubble-studio/src/hooks/usePearlChatStore.ts
  • apps/bubble-studio/src/hooks/usePearlStream.ts
  • apps/bubblelab-api/src/routes/ai.ts
  • apps/bubblelab-api/src/routes/bubble-flows.ts

Comment on lines +429 to +445
case 'stream_complete': {
// Check if the backend returned an error result (e.g. missing API key)
const streamData = event as unknown as {
coffeeResult?: { type?: string; error?: string; success?: boolean };
};
if (
streamData.coffeeResult &&
streamData.coffeeResult.success === false
) {
const errorMsg =
streamData.coffeeResult.error ||
'Generation failed. Please check your API key configuration.';
state.addEvent({ type: 'generation_error', message: errorMsg });
state.setGenerationCompleted(true); // also clears abort controller + isGenerating
}
break;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Redundant with the new error event — risks double-logging the failure.

The backend now emits a proper error SSE event before stream_complete when coffeeResult.success === false (see bubble-flows.ts lines 1423-1436). The existing case 'error' handler (lines 410-422) already calls state.addEvent({ type: 'generation_error', ... }) and state.setGenerationCompleted(true), so this block will add a second generation_error timeline event and re-trigger setGenerationCompleted for the same failure.

Recommended: remove this stream_complete error branch (the error event is now the single source of truth), or guard it so it only fires when no prior error event was received in this run.

Additionally, the coffeeResult field is read from the root of the event via event as unknown as { coffeeResult?: ... }, which bypasses StreamingEvent typing. If you keep any logic here, consider extending the shared schema so this isn't a blind cast.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/bubble-studio/src/hooks/usePearlChatStore.ts` around lines 429 - 445,
The 'stream_complete' branch currently duplicates handling already done in the
'error' SSE handler and blindly casts event to read coffeeResult; remove the
coffeeResult failure branch inside the 'case "stream_complete"' or guard it so
it only runs when no prior 'error' event was received (e.g., track a local flag
like errorEventReceived set by the existing 'case "error"' handler before
calling state.addEvent/state.setGenerationCompleted). Also avoid the blind cast
by using the shared StreamingEvent type (or extend that schema to include
coffeeResult) when reading coffeeResult to keep typings correct.

…n missing API key

Three related bugs fixed:

1. Wrong SSE error event shape in backend routes
   - bubble-flows.ts and ai.ts were sending { type: 'error', error: '...' }
   - StreamingEvent schema requires { type: 'error', data: { error, recoverable } }
   - Frontend handler reads event.data.error, so errors silently fell back to
     'An error occurred' or were swallowed entirely

2. Missing error event on coffee agent failure (planning phase)
   - When GOOGLE_API_KEY is absent, coffee.ts returns early with success:false
   - The route only sent stream_complete with the error buried in coffeeResult
   - No proper error SSE event was emitted, so the frontend never received it

3. UI stuck in infinite loading state when API key is missing
   - generationAbortController was never cleared after the stream ended
   - isGenerating stayed true forever since setGenerationCompleted was never called
   - Added safety net in startGenerationStream to always clear isGenerating after
     the stream loop exits without a generation_complete event
   - error event is now the single source of truth; stream_complete is a no-op
@HARSH048 HARSH048 force-pushed the fix/sse-error-event-format-and-infinite-loading branch from ae5de08 to 2f9a0e9 Compare April 19, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant