Workbox: queue.replayRequests() fails with TypeError: Failed to execute 'clone' on 'Request': Request body is already used

Created on 25 Mar 2019  路  3Comments  路  Source: GoogleChrome/workbox

Library Affected:
workbox.backgroundSync.Queue. (Workbox 4.1.1)

Browser & Platform:
Google Chrome v73.0.3683.86 macOS

Issue or Feature Request Description:
When queue.replayRequests() is called and the server is still down, it throws the following error and removes one item from the queue even if it wasn't synced.

sw.js:132 TypeError: Failed to execute 'clone' on 'Request': Request body is already used
    at d.g (Queue.mjs:176)
    at d.unshiftRequest (Queue.mjs:140)
    at d.replayRequests (Queue.mjs:246)

This is the code to register a route for background sync:

  const queue = new workbox.backgroundSync.Queue('myQueue')

  const addEventRoute = new workbox.routing.Route(
    ({ url }) => url.pathname === '/api/add',
    async args => {
      try {
        await fetch(args.event.request.clone())
      } catch (err) {
        console.error(err)
        queue.pushRequest(args.event)
      }
    },
    'POST'
  )

  workbox.routing.registerRoute(addEventRoute)

  self.addEventListener('sync', async ev => {
    try {
      await queue.replayRequests()
    } catch (e) {
      console.error(e)
    }
  })

When I try to manually trigger the sync from the UI with:

<Button
   variant="contained"
   onClick={() => navigator.serviceWorker.ready.then(reg => reg.sync.register('syncEvents'))}
>
sync
</Button>

it fails and removes the oldest entry from the queue.
The sync works well, when the server is reachable again but not when it is still down.

workbox-background-sync

Most helpful comment

Even if your endpoint is down it workbox is going to replay the request and pop it off the queue, regardless of the return code. I'm currently working around this too, editing the onSync logic based on this post about backgroundSync in workbox v4:

https://github.com/GoogleChrome/workbox/pull/1710

Here is my code. I'm throwing a network error because it seems to be the only one allows me to re-queue the request properly

  • made an edit, the last code block was a little buggy
const collectQueue = new workbox.backgroundSync.Queue('medsinc-collect', {
  maxRetentionTime: 60 * 24 * 30,
  onSync: async({ queue }) => {
    let entry;
    while (entry = await queue.shiftRequest()) {
      try {
        let clone = entry.request.clone()
        let response = await fetch(clone)
        if (!response.ok) {
          throw new Error()
        }
      }
      catch (e) {
        await queue.unshiftRequest(entry);
        throw {
          name: 'NetworkError',
          message: 'Server Not Available'
        }
        return
      }
    }
  }
});

All 3 comments

Even if your endpoint is down it workbox is going to replay the request and pop it off the queue, regardless of the return code. I'm currently working around this too, editing the onSync logic based on this post about backgroundSync in workbox v4:

https://github.com/GoogleChrome/workbox/pull/1710

Here is my code. I'm throwing a network error because it seems to be the only one allows me to re-queue the request properly

  • made an edit, the last code block was a little buggy
const collectQueue = new workbox.backgroundSync.Queue('medsinc-collect', {
  maxRetentionTime: 60 * 24 * 30,
  onSync: async({ queue }) => {
    let entry;
    while (entry = await queue.shiftRequest()) {
      try {
        let clone = entry.request.clone()
        let response = await fetch(clone)
        if (!response.ok) {
          throw new Error()
        }
      }
      catch (e) {
        await queue.unshiftRequest(entry);
        throw {
          name: 'NetworkError',
          message: 'Server Not Available'
        }
        return
      }
    }
  }
});

I'm experiencing the same issue in the Chrome on Android (73.0.3683.90):

Uncaught (in promise) TypeError: Failed to execute 'clone' on 'Request': Request body is already used
    at Queue._addRequest (Queue.mjs:176)
    at Queue.unshiftRequest (Queue.mjs:140)
    at Queue.replayRequests (Queue.mjs:246)

The exact same code works in Chrome on Windows (both 73.0.3683.86 and 73.0.3683.103).

My service worker code is as simple as this:

const bgSyncPlugin = new workbox.backgroundSync.Plugin('tag', {
  maxRetentionTime: 24 * 60 // Retry for max of 24 Hours
});

workbox.routing.registerRoute(
  new RegExp('.+\/a\/b'),
  new workbox.strategies.NetworkOnly({
    plugins: [bgSyncPlugin]
  }),
  'POST'
);

Thanks for the report, I've looked into it and it seems there's a place we're not cloning the request before re-fetch()-ing it. And since our tests weren't sending the request to a real server, they weren't reporting the error.

This should be fixed in #2014.

Was this page helpful?
0 / 5 - 0 ratings