@@ -113,13 +113,19 @@ export const retryFetch = async (
113113 ? applyTimeout ( init , requestTimeoutMs )
114114 : init ;
115115
116+ // Request bodies are single-use ReadableStreams. Clone Request inputs
117+ // before each attempt so retries get a fresh body stream.
118+ // this prevents errors like: "Failed to register workflow: Response body object should not be disturbed or locked"
119+ const freshInput = ( ) : Input =>
120+ input instanceof Request ? input . clone ( ) : input ;
121+
116122 let lastError : unknown ;
117123
118124 // Transport retry loop
119125 for ( let transportAttempt = 0 ; transportAttempt <= maxTransportRetries ; transportAttempt ++ ) {
120126 let response : Response ;
121127 try {
122- response = await fetchFn ( input , effectiveInit ) ;
128+ response = await fetchFn ( freshInput ( ) , effectiveInit ) ;
123129 } catch ( error ) {
124130 // Timeout/abort errors should NOT be retried
125131 if ( isTimeoutError ( error ) ) {
@@ -142,7 +148,7 @@ export const retryFetch = async (
142148 let delay = initialRetryDelay ;
143149 for ( let rlAttempt = 0 ; rlAttempt < maxRateLimitRetries ; rlAttempt ++ ) {
144150 await new Promise ( ( resolve ) => setTimeout ( resolve , withJitter ( delay ) ) ) ;
145- rateLimitResponse = await fetchFn ( input , effectiveInit ) ;
151+ rateLimitResponse = await fetchFn ( freshInput ( ) , effectiveInit ) ;
146152 if ( rateLimitResponse . status !== 429 ) {
147153 return rateLimitResponse ;
148154 }
@@ -167,7 +173,7 @@ export const retryFetch = async (
167173 headers : new Headers ( effectiveInit ?. headers ) ,
168174 } ;
169175 retryInit . headers . set ( "X-Authorization" , newToken ) ;
170- return await fetchFn ( input , retryInit ) ;
176+ return await fetchFn ( freshInput ( ) , retryInit ) ;
171177 }
172178 }
173179
0 commit comments