A UVC device you build or maintain enumerates cleanly, the first frame lands on screen, and then the pipeline freezes while the kernel log fills with protocol errors. The same unit streams all day in applications that open the device node directly, and dies within seconds in desktop apps built on PipeWire or libcamera. That combination points at one specific piece of the USB protocol: the Halt feature on the video streaming bulk endpoint and your firmware's answer to CLEAR_FEATURE(ENDPOINT_HALT). This guide is for engineers who ship their own UVC firmware or integrate board-level cameras into a product. You will read the -71 signature, separate a firmware halt bug from a cable fault, fix the request handler, and prove the fix with a USB capture before the device ships.
The symptom: one clean frame, then a dead stream
Enumeration succeeds and the application displays exactly one frame — occasionally a couple of seconds of video — and then the image stops updating. The kernel log shows a line like Non-zero status (-71) in video completion handler. from the uvcvideo driver, and on Linux -71 is the EPROTO error code: the host controller saw a protocol violation on the bulk endpoint, typically a data toggle mismatch or an unexpected response. In the traced case the device kept sending packet traffic after the application froze, which means firmware was still pushing data nobody had requested.
The pattern is deterministic, and that is the diagnostic gold. Replug the device and you get one working session again; start a second session and the stream dies the same way. If the device never streams a single frame at all, that is an enumeration or detection problem instead — start with USB camera not detected by the vision SDK on Linux.
| Observation | What it points to |
|---|---|
| One frame renders, then the image freezes | endpoint halted, recovery request mishandled |
-71 in the video completion handler |
protocol fault on the bulk endpoint, not in application code |
| Device keeps transferring after the app stops | DMA still armed, stream stop not honored |
| Clean after replug, dead on the second open | reopen masks the halt bug, keep-open exposes it |
Rank the likely causes
Order the suspects by how well each one explains a clean first frame followed by a frozen endpoint:
- No CLEAR_FEATURE(ENDPOINT_HALT) handler on the video streaming bulk endpoint. Section 9.4.5 of the USB specification requires it, and a bulk endpoint that stalls stays stalled until the host clears it. This is the prime suspect for the one-frame signature.
- The halt clears but the data toggle or data sequence number does not reset. Host and device then disagree about the next packet's sequence, and every transfer after the clear fails with -71.
- DMA left armed after the host stops the stream. The application switches sources or stops, the host expects the endpoint quiet, and stale IN traffic leaves the endpoint state inconsistent for the next session.
- Host session handling amplifies the bug. Desktop stacks that keep one camera session open across a source switch never perform the full close that resets everything. They are the messenger, not the cause.
- The physical layer. A marginal cable or an unpowered hub corrupts packets and also produces -71, but it scatters errors through a working stream instead of stopping after one clean frame.
Causes 1 to 3 are firmware work, cause 4 decides which application exposes the bug, and cause 5 costs one minute to rule out.
Why one application freezes and another streams
Applications that close the device node on exit and reopen it on the next start hand the endpoint a fresh state every time: releasing the streaming interface, and the reset that comes with it, clears the stale halt. They look flawless while carrying the same firmware bug.
Desktop camera stacks built on PipeWire and libcamera behave differently. Watch pw-top while a desktop camera application runs and the camera source stays active; when the user switches cameras, both the V4L2 source and the libcamera source hold the device open for a moment. Nothing performs the full close, so the frozen endpoint finally gets exposed. A libcamerasrc pipeline often streams once and then fails to relaunch with buffering errors, because frames from the previous session are still queued. Meanwhile libcamera's command-line test tools, cam and qcam, stream cleanly run after run, because each run stops and releases the device completely.
Freezing in exactly this pattern means the applications are innocent. The endpoint is not.
Test each cause in order
Work down the list; the first test costs a minute, the last costs a capture. Keep one known-good spare on the bench for the swap test: the LS-CBL-0219 is a 3 m USB3.0 camera cable, high-flex for drag-chain use.
- Rule out the physical layer first. Plug the camera straight into a host port, no hub, with the short known-good cable — spares live in the accessories finder. Errors that vanish with the cable were signal integrity, not protocol.
-
Watch the kernel log during the failure. Run
dmesg -worjournalctl -f, stream until the freeze, and note whether the -71 line appears at the stop or the switch, whether it appears mid-stream, and whether the device keeps transferring afterward. - Split the stacks with one pipeline each. Three GStreamer paths isolate V4L2, PipeWire and libcamera without any desktop application:
gst-launch-1.0 v4l2src device=/dev/video0 ! videoconvert ! aasink
gst-launch-1.0 pipewiresrc ! videoconvert ! aasink
gst-launch-1.0 libcamerasrc ! videoconvert ! aasink
A V4L2 path that runs clean on every launch beside a libcamera path that runs once and then fails is the session-plus-halt signature. The plain UVC baseline that must always work is covered in get a UVC camera streaming over USB without a config file.
- Capture the control transfers. A USB protocol capture spanning the stream stop and the next start is the proof. Look for the CLEAR_FEATURE request aimed at the streaming endpoint: bmRequestType 0x02 (host-to-device, standard, endpoint recipient), bRequest 0x01 (CLEAR_FEATURE), wValue 0x0000 (ENDPOINT_HALT), wIndex set to the endpoint address, 0x81 for IN endpoint 1. A healthy device acknowledges the request; a buggy one stalls the control transfer, ignores it, or acknowledges it while keeping the old toggle.
- Check the firmware's own debug log. If the control-request callback never sees bRequest 0x01 arrive, the descriptor routing or the request dispatcher is wrong; if the callback fires and streaming still fails, the handler is not resetting the toggle or sequence number.
- Try both speed tiers. Run the same open–stream–stop–reopen sequence through a USB 3 port and a USB 2 port. Firmware exercised mostly on SuperSpeed often misses the high-speed fallback, and swapping host ports is its own diagnostic — see diagnose USB camera color stream timeout across host ports.
Worked example: 1920 × 1080 at 30 fps, uncompressed
Take the stream from the failure case: 1920 × 1080 pixels at 30 fps, sent uncompressed as YUY2, the usual packed format at 2 bytes per pixel. If your firmware sends a different packing, scale the bytes-per-pixel term.
Frame size: 1920 px × 1080 px × 2 B = 4,147,200 B, about 4.1 MB per frame.
Stream rate: 4,147,200 B/frame × 30 frames/s = 124,416,000 B/s, about 124 MB/s.
Link need: 124,416,000 B/s × 8 bit/B = 995,328,000 bit/s, about 0.995 Gbit/s.
Decision: a 1 Gbit/s stream needs a SuperSpeed link. USB 3.0's 5 Gbit/s carries it with roughly five times headroom at the nominal link rate, so on USB 3 throughput is never your problem here. A high-speed USB 2.0 link is 480 Mbit/s, and bulk transfers reach about 53 MB/s of it in practice; 53,000,000 B/s ÷ 4,147,200 B/frame ≈ 12.8, so the same frame fits at most about 12 fps on high speed. Past that, compress the stream or cut the frame rate — no endpoint-level trick conjures bandwidth.
The arithmetic also explains the freeze signature. One 4.1 MB frame completes and renders, so the link, memory and buffers are all fine; the very next request then hits a halted endpoint and dies with -71. A deterministic stop after exactly one clean frame is protocol handling, not throughput.
The fix: handle CLEAR_FEATURE(ENDPOINT_HALT) in firmware
Section 9.4.5 of the USB specification settles the requirement: "The Halt feature is required to be implemented for all interrupt and bulk endpoint types." A bulk endpoint halts on error and stays halted until the host clears it, so firmware must do four things when the request arrives:
- accept the request addressed to the endpoint and acknowledge it — never stall the control transfer itself;
- clear the halt on the endpoint named in wIndex;
- restart the endpoint's sequence state: data toggle back to DATA0 on full and high speed, data sequence number back to 0 on SuperSpeed;
- re-arm IN transfers only once the host has restarted the stream.
The other half of the fix is the stop path. When the host stops the stream — for UVC, a SET_INTERFACE to alt setting 0 on the video streaming interface — stop the DMA and stop arming IN transfers immediately. The kernel's uvcvideo driver deliberately sends CLEAR_FEATURE(HALT) to the streaming bulk endpoint when a stream stops, to match what mainstream desktop hosts do; a device that only recovers when an application reopens the node is relying on that reopen to hide the bug.
If you ported a controller vendor's reference UVC firmware, diff your event handlers against its stop-streaming path. Broken ports almost always miss exactly this handler.
Prevention: the matrix that catches it before shipment
Halt bugs survive a tidy single demo and ship to customers. Run this matrix before release:
- open, stream, stop and reopen in a loop, at least twenty iterations, on both USB 3 and USB 2 ports;
- switch camera sources while a desktop stack holds the session open;
- stream once through the V4L2 path, then immediately through the libcamera path, in the same boot;
- capture the control traffic and check that every STALL on a streaming endpoint is followed by a successful CLEAR_FEATURE(ENDPOINT_HALT);
- repeat after host suspend and resume, because some hosts reset the interface on resume.
The host side has its own discipline: stop acquisition before you close, as set out in USB camera timeout on reopen: stop acquisition before you close, and watch recovery faults under load with camera acquisition stalls under load: isolate recovery faults. The systems topic page collects the rest of the streaming failure guides.
When to integrate instead of debug
Fixing firmware is the right call when the camera is your product. When the camera is a component inside a machine you build, the better decision is a maintained USB stack, with your engineering time spent on the application.
Two board-level parts cover small enclosures: the LS-BCM-0003 is a 3.1 MP USB3 board-level camera with an IMX252, 1/1.8" global shutter running 120 fps, and the MV-CB060-10UM-B is a 6 MP bare-board USB3 camera with an IMX178 mono sensor. Both mount in hardware built around the board-level cameras range.
- Sensor format
- 1/1.8"
- Lens mount
- C-mount
- Sensor format
- 1/1.8"
- Interface
- USB3
For a housed industrial unit, the LS-CMR-0166 is a 12 MP USB3 area scan camera with an IMX226, 1/1.7" rolling shutter, delivering 30 fps at 4000 × 3000.
- Sensor format
- 1/1.7"
- Interface
- USB3
- Resolution
- 12 MP
- Shutter
- Rolling
And if the UVC device itself remains a product of your own, pick streaming-first parts from the start: sensor and bridge selection for a USB UVC MJPEG camera walks the choices that decide whether the endpoint logic ends up solid. For the full housed range, start in the camera finder or compare USB3 area scan cameras.
The decision for each case
- Freeze after a stop or source switch, -71 in the log, clean again after replug. The halt handler is the bug. Implement CLEAR_FEATURE(ENDPOINT_HALT) with the toggle or sequence number reset, then pass the twenty-loop matrix.
- The same freeze, plus the device keeps streaming after the application stops. Add the alt setting 0 hard stop; the halt handler alone leaves stale DMA armed.
- Errors scattered through an otherwise working stream. Fix the physical layer first — shorter shielded cable, direct host port, no hub — and retest before touching firmware.
- Your bandwidth math lands above the bus. Move to SuperSpeed hardware, compress the stream, or cut the frame rate; nothing at the endpoint level recovers 1 Gbit/s from a 480 Mbit/s link.
- The camera is a component, not your product. Skip the firmware project and configure a supported camera in the camera configurator.
If the fix lands on your bench this week, capture one stop–start session and read the CLEAR_FEATURE exchange against the fields above; the answer is usually visible in a single trace. When it is not, the contact page reaches the applications team.
Questions
What does kernel error -71 mean for a UVC camera?
-71 is EPROTO, the kernel's protocol-error code: the host controller rejected a packet that broke the USB rules, most often a data toggle or sequence number mismatch after a stall. It differs from -110, which means the wait for a frame timed out, and -75, which means a frame overflowed its buffer. Where -110 points at scheduling and -75 at buffer sizing, -71 points at the endpoint's protocol state.
Does the fix differ between USB 2.0 and USB 3.0 links?
The request is identical; the state that must reset differs. On full- and high-speed links the endpoint restarts with its data toggle set to DATA0. On a SuperSpeed link the bulk endpoint restarts with its data sequence number at 0. Firmware should treat CLEAR_FEATURE(ENDPOINT_HALT) as a full endpoint restart in both cases, then re-arm IN transfers only once the host restarts the stream.
Does CLEAR_FEATURE(ENDPOINT_HALT) also apply to the UVC status interrupt endpoint?
Yes. The USB specification requires the Halt feature for all interrupt and bulk endpoint types, and the video control interface's status interrupt endpoint can stall like any other, for example during enumeration or a bandwidth change. If your firmware ever returns STALL on that endpoint, it must accept the matching CLEAR_FEATURE with the endpoint's address in wIndex and reset the sequence state there as well.
How do I prove the fix with a USB capture?
Capture from just before the stream stop to just after the next start, then inspect the control transfer to the streaming endpoint: bmRequestType 0x02, bRequest 0x01, wValue 0x0000 (ENDPOINT_HALT), wIndex set to the endpoint address such as 0x81. A healthy device acknowledges within the control transfer and the following bulk transfers complete. A STALLed CLEAR_FEATURE, silence, or a completed clear followed by a toggling error all mean the handler needs work.
Can a bad cable cause the same one-frame freeze?
Usually not with this signature. A marginal cable produces -71 errors scattered through an otherwise working stream, random disconnects, or enumeration trouble, not one clean frame followed by a deterministic stop. Do the swap anyway: one short, shielded cable straight into a host port, no hub. If the freeze survives the swap and the -71 line sits right after a stop or source switch, the endpoint halt handler is your bug.



