Plug a USB depth camera into a Linux machine, run your application, and dmesg produces a wall of text: a kernel warning stamped Unknown pixelformat 0x495a4e49, then, once streaming starts, uvcvideo: Non-zero status (-71) in video completion handler. If you bring up USB cameras — depth modules, industrial USB cameras, anything in the USB Video Class (UVC) — you will meet both lines, usually at the worst moment. Here is the decoder for the first line, the errno behind the second, and the bandwidth arithmetic that tells you which problem you actually have.
Two messages, two different faults
The decision comes first, because the two lines share a log and nothing else.
The Unknown pixelformat line is a naming message, not a data fault. It fires while an application enumerates formats (the VIDIOC_ENUM_FMT ioctl) and the kernel's format table has no name for a vendor-specific code your camera reports. No frame is lost. If your stream works, these warnings are noise.
The Non-zero status (-71) line is a transport fault. It fires while frames should be arriving, and it means USB packets came back broken: errno 71 is EPROTO, protocol error. The suspect list is bandwidth, cable, hub and power, in roughly that order.
A third failure hides behind both: your application refuses a format outright. That is a missing decoder in the application, not a driver fault, and it gets its own fix below.
What a FourCC actually encodes
V4L2 identifies pixel formats with a 32-bit value that packs four ASCII characters. The packing is little-endian: the first character sits in the least significant byte. With that one rule, any warning of this kind is readable with pencil and paper.
Index 0 is the first character of the name. Decode the value from the log: the low byte of 0x495A4E49 is 0x49, ASCII 'I'; byte 1 is 0x4E = 'N'; byte 2 is 0x5A = 'Z'; byte 3 is 0x49 = 'I'. The code spells 'INZI'.
Standard formats use the same scheme — YUYV, MJPG, NV12, BGR3 — which is why the kernel never warns about them: it has entries for those names. The warning exists because your camera offered a name outside that table.
Decode the warning: a worked example
Decode by splitting the value into bytes from the least significant end. One open of a UVC depth camera printed six codes in a row:
| dmesg prints | Bytes, LSB first | Decoded | Family |
|---|---|---|---|
| 0x5A564E49 | 49 4E 56 5A | INVZ | vendor depth code |
| 0x495A4E49 | 49 4E 5A 49 | INZI | vendor code, interleaved depth + IR |
| 0x52564E49 | 49 4E 56 52 | INVR | vendor code |
| 0x49524E49 | 49 4E 52 49 | INRI | vendor code, 16-bit IR |
| 0x49564E49 | 49 4E 56 49 | INVI | vendor code, 8-bit IR |
| 0x494C4552 | 52 45 4C 49 | RELI | vendor code |
These belong to a structured-light depth camera: the codes name its depth and infrared streams, including the interleaved depth+IR format. The kernel does not need those meanings; it only carries names. The pixel layout behind each code lives in the camera maker's SDK documentation. Hex case is irrelevant — the log prints lowercase, the table uppercases it for legibility.
Run the rule backwards and you can predict what dmesg will print. Take the name 'INZI': its four bytes, from the least significant end, are 0x49, 0x4E, 0x5A and 0x49:
Step by step: 0x49 + 0x4E00 = 0x4E49; 0x4E49 + 0x5A0000 = 0x5A4E49; 0x5A4E49 + 0x49000000 = 0x495A4E49 — the exact value in the log.
Ten lines of Python make the rule permanent:
# Example helper we wrote for this article, not a library API.
def decode_fourcc(value: int) -> str:
assert 0 <= value <= 0xFFFFFFFF, "pixelformat is a 32-bit value"
return "".join(chr((value >> (8 * i)) & 0xFF) for i in range(4))
# Test: the value from the log decodes to the camera's format name.
assert decode_fourcc(0x495A4E49) == "INZI"
print(decode_fourcc(0x5A564E49)) # prints INVZ
The assert passes: shifting 0x495A4E49 right by 0, 8, 16 and 24 bits yields 0x49, 0x4E, 0x5A and 0x49 — the four bytes that spell INZI.
If a stream refuses to start and a node offers nothing but codes like these, the node is usually fine — it has no standard format to offer. The case where the kernel build itself rejects a format is different, and depth camera pixel format rejected: kernel support on Linux handles that one.
Why the kernel prints it at all
A UVC camera describes its formats in USB descriptors, and each format carries a 16-byte class GUID. The uvcvideo driver maps the GUIDs the USB standard defines — uncompressed YUY2, NV12, MJPEG and friends — to V4L2 names. A vendor-specific format has no standard GUID, so the driver builds a FourCC from the camera's own bytes and hands it upward.
When an application then enumerates formats, the V4L2 core's enumeration handler (v4l_enum_fmt in v4l2-ioctl.c) finds no table entry, prints the warning, and some kernels append a full stack trace to it. The trace looks dramatic because it is a WARN, not because anything failed.
Enumeration runs the moment an application opens the device: guvcview, a browser's capture stack, ffmpeg and GStreamer all list formats on open. Open the device five times and the warnings come five times. The kernel's UVC driver documentation traces the path vendor formats and extension units take through the driver, if you want the full route through the source.
Non-zero status (-71): EPROTO while streaming
Now the second line. On Linux, errno 71 is EPROTO, protocol error. uvcvideo prints Non-zero status (-71) in video completion handler when a USB transfer carrying video data completes with that negative status, so the driver discards the packet and keeps going.
Timing is the fingerprint. The pixelformat warnings cluster at open, during enumeration; the -71 lines cluster while frames should be flowing — seconds or minutes into a session. Frames that freeze, drop or never appear alongside these lines point at the transport, not the format layer.
Ranked by how often each cause wins: the link negotiated down from SuperSpeed (5,000 Mbit/s) to high speed (480 Mbit/s) — almost always the cable; a hub that cannot carry the isochronous load; a cable that is too long or damaged; USB autosuspend putting the device to sleep mid-stream; and only then the camera itself. Check the negotiated speed first: lsusb -t prints 5000M for a healthy USB 3 link and 480M for one that fell back.
If the symptom is a black image rather than dropped frames, the USB bandwidth and power checklist for black depth-camera images covers the same suspects in symptom order, and keeping the USB link alive on long cable runs sets the cable limits behind cause number one. While testing, swap in a short, known-good USB 3 cable.
The bandwidth arithmetic that decides it
UVC streams reserve link bandwidth as isochronous transfers, a fixed slice every frame. If the link is slower than the stream, the controller drops packets and the completion handler reports exactly the -71 you see.
Worked example — both streams of a typical UVC depth camera at 30 fps:
- Depth: 640 × 480 pixels, 16 bits per pixel (2 bytes): 640 × 480 × 2 = 614,400 bytes per frame; × 30 = 18,432,000 bytes/s ≈ 18.4 MB/s ≈ 147 Mbit/s.
- Color: 1920 × 1080 pixels, 16-bit YUY2 (2 bytes per pixel): 1920 × 1080 × 2 = 4,147,200 bytes per frame; × 30 = 124,416,000 bytes/s ≈ 124.4 MB/s ≈ 995 Mbit/s.
- Together: 18.4 + 124.4 = 142.8 MB/s ≈ 1.14 Gbit/s.
Compare with the link. USB 3.0 moves 5 Gbit/s on the wire and 4 Gbit/s of payload after 8b/10b encoding — about 400 MB/s, so the pair above fits with margin to spare. USB 2.0 high-speed isochronous tops out at 3,072 bytes per 125 µs micro-frame (a 1024-byte packet up to three times per micro-frame): 3,072 × 8,000 micro-frames/s = 24,576,000 bytes/s ≈ 24.6 MB/s. The color stream alone is five times that ceiling; both streams are about six times over. On a link stuck at 480 Mbit/s the arithmetic says the stream cannot run — which is precisely the -71 in the log.
The arithmetic scales the same way for more cameras: halve the frame rate, halve the stream. For several cameras on shared ports, the USB hub frame-rate budget and the multi-camera bandwidth and power budget do the per-port math, and the stream-timeout bandwidth check turns the numbers into a pass/fail test when streams stall.
A bring-up sequence that separates noise from fault
- Open two terminals. Run
dmesg -w(orjournalctl -k -f) in one, start your application in the other, and note which messages appear at open and which appear while frames should flow. - Map the nodes with
v4l2-ctl --list-devices, which groups every/dev/videoNnode by the USB device that owns it. A depth camera commonly exposes one node per stream: color, depth, infrared, sometimes metadata. - List formats per node with
v4l2-ctl -d /dev/videoN --list-formats-extand decode every unknown code with the rule above. Expect vendor codes on the depth and infrared nodes and a standard set — YUYV, MJPG — on the color node. - Stream a standard format from the color node. If it runs while the warnings repeat, the warnings are noise; leave them alone.
- If frames freeze or vanish, read the accompanying line. A -71 in the completion handler means transport: confirm 5000M in
lsusb -t, move the camera to a root port, shorten or replace the cable, then drop streams or frame rates until the bandwidth product fits the link. - If the application rejects the format, hand the depth node to the camera maker's SDK, which knows its own codes, and keep the color node for standard tools.
When the usual answer is wrong
- Warnings, no -71, working stream: ignore the warnings. Nothing in the data path is wrong, and no patch belongs there.
- -71 with no pixelformat warnings at all: a pure transport fault. The format layer never entered the story — go straight to speed, cable, hub, power.
- An application reports no supported format on a depth node while dmesg shows vendor codes: the node works and the app lacks the decoder. The guide to diagnosing missing depth streams after an unknown-pixel-format report walks that exact fork, including when the depth node is simply the wrong
/dev/videoN.
Cameras that never meet this driver
If you are choosing hardware rather than debugging it, the whole class of message disappears with cameras that do not speak UVC. GigE Vision cameras ride the network stack: no USB, no isochronous budget, and cable runs to 100 m on standard Gigabit Ethernet. USB3 Vision cameras do use a USB cable, but they enumerate as a vendor-class device rather than a UVC video device, so uvcvideo never binds them — capture runs through the GenICam stack that ships with the camera.
Two from the shelf: the LS-CMR-0166, a 12 MP USB3 area scan camera with a 1/1.7" rolling-shutter sensor delivering 30 fps at 4000 × 3000, and the MV-CS200-10GC, a 20 MP GigE camera on a 1" sensor. GigE buys distance and immunity to USB bandwidth math; USB3 Vision keeps the GenICam workflow on a USB cable.
- Sensor format
- 1/1.7"
- Interface
- USB3
- Sensor format
- 1"
- Lens mount
- C-mount
Both appear in the area scan camera category; the camera finder filters by interface, resolution and shutter. If depth capture is the actual job, the 3D sensor category holds the structured-light and laser-profiler systems that ship with their own capture stacks. Not sure which interface fits? Start the camera configurator at the camera step.
Five checks before you call the camera broken
- Match the message to the moment: enumeration-time lines are name lookups; streaming-time errnos are transport faults.
- Decode every unknown code before you search the web for it — the byte rule turns hex into a name in seconds.
- Confirm negotiated speed with
lsusb -t; 480M on a USB 3 camera explains most -71 storms by itself. - Budget bandwidth before adding a second camera: bytes/s = width × height × bytes/pixel × fps, and a USB 2.0 isochronous endpoint tops out near 24.6 MB/s.
- Depth and infrared nodes that offer only vendor codes are working as designed; use the SDK for those streams and keep standard tools on the color node.
More bring-up fixes live on the systems topic page. Paste your dmesg excerpt through the contact page and we will read the codes with you.
Questions
What does errno -71 mean for a USB camera on Linux?
71 is EPROTO, protocol error, reported by the kernel as a negative errno. uvcvideo prints it when a USB transfer carrying video data completes in a broken state, so the packet contents are discarded. It is a transport symptom, seen while the stream should be running: the usual fixes are confirming SuperSpeed negotiation with lsusb -t, moving to a root port, shortening the cable and freeing hub bandwidth. Format enumeration warnings are unrelated to it.
How do you list a camera's formats without opening it in a GUI app?
Use v4l2-ctl from the v4l-utils package. 'v4l2-ctl -d /dev/video2 --list-formats-ext' prints every format the node offers, with its FourCC and the frame sizes and rates for each. Run 'v4l2-ctl --list-devices' first to see which node belongs to which camera. A kernel may still log one unknown-pixelformat line while the enumeration ioctl runs; that is the same name lookup as before, not a new fault.
Is it safe to ignore the warning when the stream works?
Yes. The line reports that the kernel's format table has no name for a code the camera offered during enumeration; no pixel data touched that code path. Watch instead for messages stamped while frames should be flowing: completion-handler status lines such as -71, disconnect events, or bandwidth errors. A stream that runs clean for hours alongside repeating enumeration warnings needs no fix, and nothing in the data path degrades because the name is unknown.
Why does one USB camera create several /dev/video nodes?
A UVC depth camera usually exposes one video node per stream: color, depth, infrared, and sometimes a metadata node on top. The lowest number is commonly the color node, but the order is not guaranteed. v4l2-ctl --list-devices groups the nodes by physical device so you can tell which is which, and --list-formats-ext per node shows what each offers. Pointing an application at the wrong node is the most common reason it claims the camera has no supported format.
Your depth node offers only codes the kernel calls unknown — is the camera defective?
No. The depth and infrared streams of UVC depth cameras are vendor-specific formats by design, so those nodes can legitimately carry nothing but unknown codes. The camera is fine if the vendor's SDK streams from those nodes and the color node offers standard formats. A defect looks different: no video nodes appear at all, or the completion handler reports errors during streaming. Use the SDK for depth work and reserve standard tools for the color stream.

