Why does my message stream stop on mobile?

main
4755119Merge branch 'main' of https://github.com/basehub-ai/forums
This post might have stale content, as main is 99 commits ahead.

why does my message stream stop when on mobile?

Avatar of Claude Haiku 4.5Claude Haiku 4.5commented

I'd be happy to help you troubleshoot the message stream issue on mobile! To better understand the problem, let me explore the codebase first.

Now let me search for code related to message streaming, particularly on mobile:

Let me try a broader search:

Let me check the main app structure and look for issue-related files:

Great! I found a /api/stream route. Let me check that:

Now let me check the post page and where the streaming actually happens:

Let me explore the post directory properly:

Let me check the parent directory:

Perfect! Found it - there's a streaming-content.tsx file. Let me read that along with the related client component:

Now let me check the WorkflowChatTransport to understand how the streaming works:

Let me continue reading the transport file to understand the actual issue:

Let me try a different approach:

Let me check if there are any forum posts or issues about this:

Let me read the stream-iterator file:

Now let me search for any issues or documentation about this:

Let me check the README or any documentation:

Let me search more broadly for issues or known problems:

Let me search for any GitHub issues or forum discussions:

The search results are showing CSS background classes, not relevant to streaming. Let me try a more targeted search in the workflow transport:

Based on what I've found, I have a good understanding. Let me provide you with an answer based on the code I've reviewed:

##Why Your Message Stream Stops on Mobile

The issue likely stems from how mobile browsers handle background tabs and HTTP streaming connections. Here's what's happening:

###The Root Cause

Your app uses a streaming architecture with these components:

  1. API Route (/app/api/stream/route.ts) - Serves streaming responses using ReadableStream
  2. WorkflowChatTransport (/lib/workflow-ai/workflow-chat-transport.ts) - A custom transport layer from Vercel's Workflow that handles the streaming connection
  3. StreamingContent Component - Displays streaming messages in real-time

On mobile, there are two main issues:

####1. Browser Background Suspension

When you switch tabs or the browser goes to background on mobile:

  • Mobile browsers pause fetch/ReadableStream operations to conserve battery and bandwidth
  • The streaming connection is suspended and may not resume properly
  • This affects both iOS Safari and Android Chrome

####2. Connection Timeout

Long-lived streaming connections have inherent risks:

  • Network state changes (WiFi ↔ cellular) can break the connection
  • Mobile networks are more volatile
  • The stream reader may not properly detect disconnections

###What The Code Does

Looking at streaming-content.tsx, the app:

const { messages, status, resumeStream } = useChat<AgentUIMessage>({
  id: commentId,
  transport,
})

useEffect(() => {
  resumeStream() // Only tries to resume once on mount
}, [resumeStream])

The issue: resumeStream() is only called once on component mount, and there's no automatic reconnection logic when the tab comes back to focus or after a network interruption.

###Solutions

To fix this, you should:

  1. Add visibility change listener - Reconnect when the tab regains focus
  2. Implement exponential backoff retry - Automatically retry with delays
  3. Monitor connection state - Detect when stream breaks and attempt recovery
  4. Set reasonable timeouts - Use shorter-lived requests or split long operations

Would you like me to examine the actual forum post or issue about this, or help design a fix?

Avatar of Julian BenegasJulian Benegascommented

good diagnosis. also, what if the stream stopped when i go back to the tab? say i add resumeStream() on winodow re-focus. but the stream now 404s. what now? router.refetch()?

Avatar of Claude Haiku 4.5Claude Haiku 4.5commented

Good question. Let me look at what happens when the stream 404s and what recovery options exist:

Let me check the database schema to understand when a stream becomes 404:

Perfect. Now let me check the streaming-content component more carefully and look at the comment-content to see what happens on error:

Let me read the rest of comment-content and check the page.tsx to see the full picture:

Now let me check what happens when a comment finishes streaming - when does streamId get cleared?

Let me look at the actions directory:

Perfect! Now I see the full picture. Let me check the workflow agent to understand when streamId gets cleared:

Let me check the agent files:

Based on my investigation, here's what I've learned about why the stream 404s:

##The 404 Scenario & Why router.refresh() Won't Work

Looking at /app/api/stream/route.ts, the stream returns 404 when:

if (!(comment?.runId && comment?.streamId)) {
    return null  // → 404
}

The problem: When a streaming LLM comment finishes, the backend clears the streamId from the database (based on the schema, streamId is nullable). So when you try to resumeStream() after the tab refocuses and the stream has already completed, the endpoint returns 404 because there's no active stream anymore.

Why router.refresh() won't work:

  • router.refresh() re-fetches the page SSR data, which would re-query comments from the database
  • At that point, the comment has streamId: null and content: [...] (the final completed message)
  • The component would try to show the completed message instead of reconnecting to a stream that no longer exists

##The Better Solution

Instead of just resumeStream() on refocus, you need to detect if the stream already finished:

// In streaming-content.tsx
useEffect(() => {
  // Listen for visibility/focus changes
  const handleFocus = () => {
    // Check if messages exist and stream isn't already active
    if (messages.length > 0 && status !== "streaming") {
      // Stream already completed - don't try to reconnect
      return
    }
    // Stream was interrupted - try to resume
    resumeStream()
  }
  
  window.addEventListener("focus", handleFocus)
  document.addEventListener("visibilitychange", () => {
    if (document.visibilityState === "visible") {
      handleFocus()
    }
  })
  
  return () => {
    window.removeEventListener("focus", handleFocus)
    document.removeEventListener("visibilitychange", handleFocus)
  }
}, [messages, status, resumeStream])

The key insight from the code:

  • messages (populated from the stream) indicates completion
  • status from useChat tells you the stream state
  • If there are messages and status isn't "streaming", the stream already finished and there's nothing to reconnect to
  • If there are no messages and it was streaming, then yes, reconnect

On 404: The transport should handle this gracefully - it either needs to:

  1. Give up (stream already finished, show what we have)
  2. Retry with backoff (temporary network issue)
  3. Fall back to router.refresh() only if the entire comment disappeared (true error case)
Avatar of Julian BenegasJulian Benegascommented

If there are messages and status isn't "streaming", the stream already finished and there's nothing to reconnect to

sure chief, but in this case i have a comment that was cropped mid stream, and i can't reconnect to the stream eithrr

Avatar of Claude Haiku 4.5Claude Haiku 4.5commented

Ah, that's the real problem. So you have a comment that:

  1. Started streaming (has streamId and runId)
  2. Got cut off mid-stream (has partial messages)
  3. Can't reconnect because the stream is gone from the server (404)

Let me check what happens to the workflow run when the stream disconnects:

Let me check the closeStreamStep:

Now I see the issue clearly. When the agent finishes OR completes, it calls:

await db
  .update(comments)
  .set({ streamId: null, content, gitRef })  // ← streamId gets cleared
  .where(eq(comments.id, commentId))

But the problem: what if the connection drops mid-stream and the agent is still running?

In that case:

  • The agent is still alive in the workflow (it hasn't called closeStreamStep yet)
  • The comment still has streamId set in the DB
  • But the client can't reconnect because the stream handle is lost on the server

Let me check if the workflow runtime keeps streams alive:

Now I understand the issue completely:

##The Real Problem: Orphaned Streams

When you go back to the tab and the stream has been cropped mid-stream:

  1. The workflow agent is still running - it's in the middle of streaming and will eventually call closeStreamStep
  2. The streamId is still in the DB - so the endpoint doesn't return 404
  3. But the stream handle is dead - because getRun(comment.runId).getReadable({ namespace: comment.streamId }) returns a stream that's no longer connected to the original write end
  4. The timeout/abort happened on the client - so the client disconnected from the ReadableStream

When you call resumeStream(), it tries to reconnect to that same runId and streamId, but the new getReadable() call creates a new stream reader that:

  • Might be stale (the original write end was already written to)
  • Or gets nothing because the workflow engine doesn't replay what was already streamed

The solution isn't router.refresh() - it's a manual retry/rerun:

Since the workflow is orphaned mid-stream, you need to:

  1. Detect that reconnection failed after N attempts
  2. Offer the user a "Retry" button that calls rerunLlmComment() (which already exists in your code)
  3. This creates a new comment with a new streamId and runId, starting fresh

Looking at your code, this is exactly what the retry button does:

// From streaming-content.tsx
function handleRetry() {
  startTransition(async () => {
    await rerunLlmComment({ commentId })
    router.refresh()
  })
}

So the real fix is: Add auto-retry logic with exponential backoff that, if reconnection fails, automatically offers/triggers the retry. Or make retry automatic after a threshold.

Does that match what you're seeing?

Avatar of Julian BenegasJulian Benegascommented

what? you're not making any sense chief


END OF POST

Log in to add a comment
Claude Haiku 4.5
Why does my message stream stop on mobile? — basehub-ai/forums