trpc / trpc/trpc

feat: Controlling retry behavior of `useSubscription`

Open
#7,064 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
40.6k
Forks
1.7k
Avg merge
1d 22h
Merged PRs (30d)
22

Description

Describe the feature you'd like to request

Allow controlling retry behavior for httpSubscriptionLink on a per-request basis via the useSubscription hook options. Currently, httpSubscriptionLink automatically retries on certain error codes (INTERNAL_SERVER_ERROR, BAD_GATEWAY, SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT) without any way to opt out. This retry logic seems to be hardcoded in the link and happens before onError is called, making it impossible to prevent from the call site. For subscriptions that trigger side effects (e.g., sending a message to an AI conversation), automatic retries can cause duplicate operations on the backend.

Describe the solution you'd like to see

Maybe add a retry option directly to useSubscription:

api.conversation.streamMessages.useSubscription(
  { conversationId },
  {
    onData: (data) => { /* ... */ },
    onError: (error) => { /* ... */ },
    retry: false // or a function: (error) => boolean
  }
);
Describe alternate solutions

I tried a few things without finding success.

  1. Using the retryLink and checking for an error code. I updated my backend (what the subscription ends up calling) to simply respond with a 500 for testing and the retryLink wasn't even hit. So I don't think I can control it? The event source data contains:
event: connected
data: {}

event: serialized-error
data: { /* a big ol json blob */ }

Here is what my retryLink:

splitLink({
    condition: (op) => op.type === 'subscription',
    true: [
        retryLink({
            retry: (opts) => {
                console.log('retrying'); // This is not logged
                opts.op.type;
                const code = opts.error.data?.code;
                if (!code) {
                    console.error('No error code found, retrying');
                    return true;
                }
                if (code === 'INTERNAL_SERVER_ERROR') {
                    return false;
                }
                return false;
            }
        }),
        httpSubscriptionLink({
            url: `${getBaseUrl()}/api/trpc`,
            transformer: superjson
        })
    ],
    false: httpBatchLink({
        url: `${getBaseUrl()}/api/trpc`,
        transformer: superjson
    })
})
  1. I also attempted to use a useEffect in the component where my useSubscription is established. I'm able to prevent retries here, but I don't seem to be able to discern the error code to check for only INTERNAL_SERVER_ERROR. This might be user error on my part, but inspected everything about the error and didn't see access to a code.
    useEffect(() => {
        if (subscription.status === 'connecting' && subscription.error) {
            subscription.reset();
            resetStreamingState(subscription.error.message || 'An error occurred while processing your message.');
        }
    }, [subscription, resetStreamingState]);
Additional information

My use case here is:

  • React page uses a subscription to send a message to a chat bot backend type thing. Works great
  • If an error occurs during the request (you close your laptop or the server responds with a 500), the subscription retries automatically and the message is sent to the chat again
  • I want to prevent retrying in certain cases

As far as I can tell, the event source event: serialized-error prevents me from using the existing tooling tRPC provides for controlling this.

Thanks so much for your time and attention! Huge fan of the library and appreciate all you do :)

👨‍👧‍👦 Contributing
  • 🙋‍♂️ Yes, I'd be down to file a PR implementing this feature!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the useSubscription options and trace how httpSubscriptionLink handles the listed retryable errors, including the serialized-error event and the point before onError. Compare that path with retryLink. Done means a per-request false or predicate option can control retries without breaking existing behavior, with coverage for the requested error cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
api
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.