You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug] Failed to reset USB device: NetworkError (Windows) #2417
changed the title [-][Bug] USB connection to primehub does not complete[/-][+][Bug] USB connection to primehub does not complete (Windows)[/+]on Nov 2, 2025
changed the title [-][Bug] USB connection to primehub does not complete (Windows)[/-][+][Bug] Failed to reset USB device: NetworkError (Windows)[/+]on Dec 5, 2025
It turns out that WINUSB has much more limited features compared to other OSes. So even some functions in the WebUSB implementation, like reset() are not available on Windows.
I started looking into this a while back but I didn't come up with a solution yet. The reason we were using the reset in the first place is because endpoints have an even/odd thing where every other packet is marked as even (and the rest odd) to catch missed or duplicate packets. If the host and the peripheral get out of sync, it causes an error. The reset is one way to sync these back up.
Instead, I think we will have to do something with stalls and clearing the stalls on the endpoint to sync them back up.
pybricksdev has similar issues on Windows for the same reason.
It turns out that WINUSB has much more limited features compared to other OSes. So even some functions in the WebUSB implementation, like reset() are not available on Windows.
I started looking into this a while back but I didn't come up with a solution yet. The reason we were using the reset in the first place is because endpoints have an even/odd thing where every other packet is marked as even (and the rest odd) to catch missed or duplicate packets. If the host and the peripheral get out of sync, it causes an error. The reset is one way to sync these back up.
If I remove the problematic part from Pybricks Code:
- // Always reset the USB device to ensure it is in a known state.- const [, resetErr] = yield* call(() => maybe(usbDevice.reset()));- if (resetErr) {- // TODO: show error message to user here- console.error('Failed to reset USB device:', resetErr);- yield* put(usbDidFailToConnectPybricks());- yield* cleanup();- return;- }
Everything still works on my Linux host.
@dlech: Where are we doing this "even/odd" thing mentioned above? I'd like to try and force the bad case to see if we can recover.
Instead, I think we will have to do something with stalls and clearing the stalls on the endpoint to sync them back up.
We do already have timeouts on the send in pbdrv_usb_tx_event which leads to calling pbdrv_usb_tx_reset. Might this be sufficient?
From what I'm reading, calling reset from the host is somewhat blunt and typically not needed?
@jaguilar: I think you already have a build environment for Pybricks Code on Windows. Would you be able to try the above patch to see if it fixes connecting/downloading/running on Windows? Thanks!
Where are we doing this "even/odd" thing mentioned above?
It is in the endpoints. I'm not sure if it is in software or hardware. But clearing a stall/halt should reset just the endpoints.
There is a 50% chance that if you disconnect and reconnect that the endpoints will be in the right state. So you need a very specific test that does an odd number of packets to trigger the bug. I was able to do this with pybricksdev by downloading and running a program of a specific size.
From what I'm reading, calling reset from the host is somewhat blunt and typically not needed?
Yes, there should be other ways around it.
For example, we could make our own reset command that just resets the endpoints. Search the code for MSC_BOT_Reset to see an example in the STM32 USB library. The Microsoft docs recommended doing something like this.
We do already have timeouts on the send in pbdrv_usb_tx_event which leads to calling pbdrv_usb_tx_reset. Might this be sufficient?
This does not reset the endpoints, so no, it is not sufficient. Possibly we could do that, but I don't think it is the right solution.
Additionally, there is no way for the host computer to know that the hub has timed out and that it needs to re-subscribe. Even if the endpoints were reset, the hub would still stop sending data to the host. The host can't tell the difference between a timeout and just no data. So it is a separate problem that also needs to be fixed.
We either need to make the timeout not touch USB and only drop stdout data so that we don't block print functions or we need to change how it works and call USBD_LL_StallEP() (or platform equivalent) so that the host knows that the hub timed out and needs to be reset again. Or we could send a "heartbeat" and the host would be responsible for keeping track of that and having it's own timeout and resetting after that.
I'm a bit in favor of at least trying the first option first since it is much easier to implement. Then we can do some testing to see how often we are dropping messages to see if we need to try something else.
We need to test things like what if the web page "goes to sleep", does it keep servicing USB? If it stops for a while, do we care that we lost print output during this time?
Or we could consider a drastically different approach.
I initially discounted the idea of using a USB serial port implementation for a couple of reasons. The main reason is that it is what LEGO uses on SPIKE Prime and we don't want Pybricks hubs showing up in the official LEGO apps and have people complaining to LEGO that it isn't working. And also because serial port data is a stream rather than packet-based, so we would have to do more work to encode and decode packets from a stream.
Since we are looking at RFCOMM for the Pybricks Profile on Bluetooth Classic, we are going to have to do the extra work for the last point anyway.
And due to some new information we've learned recently about SPIKE Prime, I think LEGO might have changed their app in a way that might be able to discriminate between a hub running Pybricks or not. We would need to test that to be sure, but that might make the first point no longer a blocker.
This would move the handling of the endpoints out of the browser, so it could potentially solve some of those issues. I'm not sure how it would handle back-pressure though if the host-side app stopped reading from the serial port. And of course it could have some surprise issues that we don't know about since we haven't done much with Web Serial yet.
So maybe it could be prudent to try out the RFCOMM implementation first and see how that goes, then come back to USB to make a more informed decision?
@jaguilar: I think you already have a build environment for Pybricks Code on Windows. Would you be able to try the above patch to see if it fixes connecting/downloading/running on Windows? Thanks!
I have changed my main OS over to Linux but I do still have a Windows partition. What specific fixes do you want me to try and what is the test protocol (i.e. what action should work with the fixes and won't work without?).
At the moment, connecting to the hub on Windows fails as described in the first two posts above. Apparently because the reset does not work well on Windows. I was curious to see where things stand on Windows otherwise if we skipped the reset:
diff --git a/src/usb/sagas.ts b/src/usb/sagas.ts
index 3e9ae654..dd364b43 100644
--- a/src/usb/sagas.ts+++ b/src/usb/sagas.ts@@ -140,6 +140,8 @@ function* handleUsbConnectPybricks(hotPlugDevice?: USBDevice): Generator {
usbDevice = hotPlugDevice;
}
+ yield* delay(100);+
const [, openErr] = yield* call(() => maybe(usbDevice.open()));
if (openErr) {
// TODO: show error message to user here
@@ -151,16 +153,6 @@ function* handleUsbConnectPybricks(hotPlugDevice?: USBDevice): Generator {
exitStack.push(() => usbDevice.close().catch(console.debug));
- // Always reset the USB device to ensure it is in a known state.- const [, resetErr] = yield* call(() => maybe(usbDevice.reset()));- if (resetErr) {- // TODO: show error message to user here- console.error('Failed to reset USB device:', resetErr);- yield* put(usbDidFailToConnectPybricks());- yield* cleanup();- return;- }-
const [, selectErr] = yield* call(() => maybe(usbDevice.selectConfiguration(1)));
if (selectErr) {
// TODO: show error message to user here
This patch does that. (The 100ms delay here is related to a secondary issue but is worth considering in this test.)
As David explains above, this isn't necessarily how we'd move forward. But nobody has tested USB connectivity with Pybricks Code on Windows yet. Maybe there are other issues past this point that we don't know about yet, so investigating that would be quite useful.
A good test scenario here would be to try out the REPL and run a few programs.
Where are we doing this "even/odd" thing mentioned above?
It is in the endpoints. I'm not sure if it is in software or hardware. But clearing a stall/halt should reset just the endpoints.
There is a 50% chance that if you disconnect and reconnect that the endpoints will be in the right state. So you need a very specific test that does an odd number of packets to trigger the bug. I was able to do this with pybricksdev by downloading and running a program of a specific size.
I have tried to reproduce this and kept track of the number of messages we send to the hub, and alternating between making the program just a bit longer to trigger an extra packet.
So far everything works great without the reset.
Did you reproduce it back then on Spike or EV3? And do the number of packets from the hub to Pybricks Code matter too?
I made pybricks/pybricksdev#133 to get rid of the reset in pybricksdev and replace it with just resetting the endpoints (by calling the clear halt function).
Looking at Wireshark USB capture on Linux, it doesn't appear that we do the same in the browser. When a tab with Pybricks code is closed, there is an extra USB message that I'm guessing is stalling the endpoint. Then we Pybricks Code reconnects, the browser is taking care of the necessary clearing of the stall behind the scenes.
Anyway, the initial issue here about reset failing on Windows is fixed in pybricks/pybricks-code@3abc3f3. So we can close this issue and start new ones for additional USB bugs we find.
Activity
[-][Bug] USB connection to primehub does not complete[/-][+][Bug] USB connection to primehub does not complete (Windows)[/+][-][Bug] USB connection to primehub does not complete (Windows)[/-][+][Bug] Failed to reset USB device: NetworkError (Windows)[/+]laurensvalk commentedon Dec 5, 2025
Can confirm. Here we can also see that we do get disconnection events.
dlech commentedon Dec 5, 2025
It turns out that WINUSB has much more limited features compared to other OSes. So even some functions in the WebUSB implementation, like
reset()are not available on Windows.I started looking into this a while back but I didn't come up with a solution yet. The reason we were using the reset in the first place is because endpoints have an even/odd thing where every other packet is marked as even (and the rest odd) to catch missed or duplicate packets. If the host and the peripheral get out of sync, it causes an error. The reset is one way to sync these back up.
Instead, I think we will have to do something with stalls and clearing the stalls on the endpoint to sync them back up.
pybricksdev has similar issues on Windows for the same reason.
BertLindeman commentedon Dec 5, 2025
I did not see errors on win 11(25H2) using
pybricksdev run usb myprog.pyto an EV3,but on flash to the same EV3:
dlech commentedon Dec 5, 2025
I think that is #2375.
Sirus-7 commentedon Dec 7, 2025
I can confirm this issue for EV3 on Windows 10 22H2.
laurensvalk commentedon Jan 21, 2026
Let's try to make some progress here.
If I remove the problematic part from Pybricks Code:
Everything still works on my Linux host.
@dlech: Where are we doing this "even/odd" thing mentioned above? I'd like to try and force the bad case to see if we can recover.
We do already have timeouts on the send in
pbdrv_usb_tx_eventwhich leads to callingpbdrv_usb_tx_reset. Might this be sufficient?From what I'm reading, calling reset from the host is somewhat blunt and typically not needed?
@jaguilar: I think you already have a build environment for Pybricks Code on Windows. Would you be able to try the above patch to see if it fixes connecting/downloading/running on Windows? Thanks!
dlech commentedon Jan 21, 2026
It is in the endpoints. I'm not sure if it is in software or hardware. But clearing a stall/halt should reset just the endpoints.
There is a 50% chance that if you disconnect and reconnect that the endpoints will be in the right state. So you need a very specific test that does an odd number of packets to trigger the bug. I was able to do this with pybricksdev by downloading and running a program of a specific size.
Yes, there should be other ways around it.
For example, we could make our own reset command that just resets the endpoints. Search the code for
MSC_BOT_Resetto see an example in the STM32 USB library. The Microsoft docs recommended doing something like this.This does not reset the endpoints, so no, it is not sufficient. Possibly we could do that, but I don't think it is the right solution.
Additionally, there is no way for the host computer to know that the hub has timed out and that it needs to re-subscribe. Even if the endpoints were reset, the hub would still stop sending data to the host. The host can't tell the difference between a timeout and just no data. So it is a separate problem that also needs to be fixed.
We either need to make the timeout not touch USB and only drop stdout data so that we don't block print functions or we need to change how it works and call
USBD_LL_StallEP()(or platform equivalent) so that the host knows that the hub timed out and needs to be reset again. Or we could send a "heartbeat" and the host would be responsible for keeping track of that and having it's own timeout and resetting after that.I'm a bit in favor of at least trying the first option first since it is much easier to implement. Then we can do some testing to see how often we are dropping messages to see if we need to try something else.
We need to test things like what if the web page "goes to sleep", does it keep servicing USB? If it stops for a while, do we care that we lost print output during this time?
dlech commentedon Jan 21, 2026
Or we could consider a drastically different approach.
I initially discounted the idea of using a USB serial port implementation for a couple of reasons. The main reason is that it is what LEGO uses on SPIKE Prime and we don't want Pybricks hubs showing up in the official LEGO apps and have people complaining to LEGO that it isn't working. And also because serial port data is a stream rather than packet-based, so we would have to do more work to encode and decode packets from a stream.
Since we are looking at RFCOMM for the Pybricks Profile on Bluetooth Classic, we are going to have to do the extra work for the last point anyway.
And due to some new information we've learned recently about SPIKE Prime, I think LEGO might have changed their app in a way that might be able to discriminate between a hub running Pybricks or not. We would need to test that to be sure, but that might make the first point no longer a blocker.
This would move the handling of the endpoints out of the browser, so it could potentially solve some of those issues. I'm not sure how it would handle back-pressure though if the host-side app stopped reading from the serial port. And of course it could have some surprise issues that we don't know about since we haven't done much with Web Serial yet.
So maybe it could be prudent to try out the RFCOMM implementation first and see how that goes, then come back to USB to make a more informed decision?
jaguilar commentedon Jan 22, 2026
I have changed my main OS over to Linux but I do still have a Windows partition. What specific fixes do you want me to try and what is the test protocol (i.e. what action should work with the fixes and won't work without?).
laurensvalk commentedon Jan 22, 2026
At the moment, connecting to the hub on Windows fails as described in the first two posts above. Apparently because the reset does not work well on Windows. I was curious to see where things stand on Windows otherwise if we skipped the reset:
This patch does that. (The
100msdelay here is related to a secondary issue but is worth considering in this test.)As David explains above, this isn't necessarily how we'd move forward. But nobody has tested USB connectivity with Pybricks Code on Windows yet. Maybe there are other issues past this point that we don't know about yet, so investigating that would be quite useful.
A good test scenario here would be to try out the REPL and run a few programs.
laurensvalk commentedon Jan 23, 2026
I have tried to reproduce this and kept track of the number of messages we send to the hub, and alternating between making the program just a bit longer to trigger an extra packet.
So far everything works great without the reset.
Did you reproduce it back then on Spike or EV3? And do the number of packets from the hub to Pybricks Code matter too?
dlech commentedon Jan 23, 2026
I was probably using SPIKE Prime + pybricksdev. I'll try to do some more testing this weekend to refresh my memory.
dlech commentedon Jan 24, 2026
Reproducible case on Linux (didn't try other OS yet) + SPIKE Prime (didn't try EV3/NXT yet):
PBDRV_CONFIG_USB_CHARGE_ONLYset to 0.self._device.reset()commented out inPybricksHubUSB._client_connect().Run the command:
echo "print('hello')" | pybricksdev run usb -First time, works. Second time hangs forever.
dlech commentedon Jan 24, 2026
I made pybricks/pybricksdev#133 to get rid of the reset in pybricksdev and replace it with just resetting the endpoints (by calling the clear halt function).
Looking at Wireshark USB capture on Linux, it doesn't appear that we do the same in the browser. When a tab with Pybricks code is closed, there is an extra USB message that I'm guessing is stalling the endpoint. Then we Pybricks Code reconnects, the browser is taking care of the necessary clearing of the stall behind the scenes.
Anyway, the initial issue here about reset failing on Windows is fixed in pybricks/pybricks-code@3abc3f3. So we can close this issue and start new ones for additional USB bugs we find.