3D visionTroubleshooting

VIDIOC_S_FMT device or resource busy: two nodes opening one camera

Why VIDIOC_S_FMT fails with Device or resource busy: find the process or launch setting that claims the camera twice and give each camera its own serial.

LumaSpec Vision9 min read Share on LinkedIn

On this page 10 sections
  1. 01What EBUSY on VIDIOC_S_FMT means
  2. 02Cause 1: the launch never assigned the serials
  3. 03Worked example: two depth cameras, one node claimed twice
  4. 04Cause 2: a process from earlier still holds the node
  5. 05Cause 3: the second camera is the first camera's other node
  6. 06Rule out the neighbouring errors first
  7. 07Prevention: one claim per node, by design
  8. 08Order of checks, start to finish
  9. 09Where to go next
  10. 10Questions

The launch starts, the first camera comes up, and the second node throws xioctl(VIDIOC_S_FMT) failed: Device or resource busy. Nothing is wrong with the format you asked for. The error says the device is busy, and it almost always is: something already holds that /dev/video node. On a two-camera rig the usual holder is your own first node instance, because the launch file had no per-camera serial number and both instances picked the same camera. This guide shows how to read the EBUSY error, find the process that holds the node, give each camera its own serial, and set the rig up so the conflict cannot return.

What EBUSY on VIDIOC_S_FMT means

VIDIOC_S_FMT is the ioctl that negotiates the capture format: pixel format, width, height. Your program opened the device fine — open() succeeds even on a busy camera — and failed only when it tried to set the format. The kernel's V4L2 documentation states the rule in one line: when I/O is already in progress or the resource is otherwise unavailable, drivers return the EBUSY error code.

So read EBUSY as a claim conflict, not a bad format. An unsupported pixel format or size comes back as EINVAL. A permission problem comes back as EACCES. EBUSY has one meaning here: another open file handle already uses that node as a capture device. Chase the holder, not the format.

Cause 1: the launch never assigned the serials

Most two-camera rigs fail here. A multi-camera launch starts one node instance per camera, and each instance must know which physical camera to open. When the serial parameter is unset, every instance falls back to the first matching device. Both instances then open the same camera: the first streams it, and the second fails at VIDIOC_S_FMT with Device or resource busy.

Pass one serial per instance. In the launch call it takes this shape — read the exact parameter names in your camera driver's launch file, because they differ between packages:

serial_no_camera1:=<serial number of the first camera>
serial_no_camera2:=<serial number of the second camera>

To find the serials, use either command:

v4l2-ctl --list-devices
udevadm info --query=property --name=/dev/video0 | grep ID_SERIAL

v4l2-ctl --list-devices prints every video node grouped by camera, which tells you which indices belong to which unit. udevadm prints the USB serial for one node. Camera SDKs also ship an enumeration tool that lists serials; use whichever is closest to hand.

Worked example: two depth cameras, one node claimed twice

The inputs come from the failing case: two depth cameras on one host, a launch with two node instances, serial parameters unset. The rule comes from the kernel's V4L2 spec: one consumer streams a capture node, and a second claim returns EBUSY. The node indices below are illustrative — a depth camera usually registers several video nodes (depth, color, infrared), typically 3 to 5, so your numbers will differ, but the count works the same.

Count claims per node with the serials unset:

  • Instance 1 resolves to the first matching camera, /dev/video0 → claims on /dev/video0 = 1
  • Instance 2 resolves to the same first match, /dev/video0 → claims = 1 + 1 = 2
  • Limit = 1 streamer per node → 2 > 1 → instance 2's VIDIOC_S_FMT returns EBUSY

Count again with the serials set:

  • Instance 1, camera A → /dev/video0 → 1 claim
  • Instance 2, camera B → /dev/video4 → 1 claim
  • 1 ≤ 1 on every node → both format sets pass and both streams run
Serial parameter Instance 1 opens Instance 2 opens Claims on busiest node Result
unset /dev/video0 /dev/video0 2 against a limit of 1 second S_FMT fails with EBUSY
set for both /dev/video0 (camera A) /dev/video4 (camera B) 1 on every node both cameras stream

The decision this count gives: after any multi-camera launch, every capture node must carry exactly one claim. Two claims on one node means camera selection failed. Zero claims on a node you expected to stream means the serial or the node index is wrong. Recount whenever you add a camera or move a USB plug.

Cause 2: a process from earlier still holds the node

The second most common holder is leftover software: a viewer window opened to check the image, a previous launch that never shut down cleanly, a second copy of your grab loop, or an SDK preview on the same node. Linux lets many processes open the node, and only one gets to stream it, so the newcomer fails at format set.

Search the exact node the error names:

fuser -v /dev/video0
lsof /dev/video0

fuser prints the PIDs holding the node; lsof adds the command names. Confirm what you found with ps -fp <pid>, then close it the way it expects to be closed.

If both tools show nothing and the node still reports busy, the driver is in a stale state, usually after a crash. Reload the driver module or reboot once — the recount after either takes under a minute — and the claim count resets. Some SDKs keep their own lock files under /dev/shm, and a stale one from another user produces the same symptom — the stale device lock article walks that case.

The same one-holder rule appears outside V4L2. On GigE cameras a second tool gets an exclusive-control failure instead, which the GigE exclusive-control article explains.

Cause 3: the second camera is the first camera's other node

Depth and UVC-style cameras usually register more than one video node each: a capture node plus sibling nodes for metadata and the extra streams. It is easy to read /dev/video1 as camera B when it is really camera A's second node. Open that sibling and ask it for a capture format, and you can get the same EBUSY, because the node is tied to camera A's session.

v4l2-ctl --list-devices settles it, because it prints nodes grouped per camera. Select by serial number rather than index — selecting multiple depth cameras by serial number shows the selection methods for each SDK.

On some driver stacks the second opener fails even earlier, at the USB interface itself. That variant has its own fixes, covered in the USB interface-claim article.

Rule out the neighbouring errors first

Not every startup failure is EBUSY, and each errno sends you to a different fix:

  • EINVAL — the format or resolution is not one this node offers. List the node's choices with v4l2-ctl --list-formats-ext --device=/dev/video0 and match one.
  • EACCES — Permission denied points at udev rules or group membership, not a claim conflict. The video and media node permission checks cover the fix.
  • ENODEV — the node vanished because the device dropped off the bus, or a second application's open and close raced yours. The disappearing device nodes article covers that race.

EBUSY alone means a holder exists. Everything else has its own path.

Prevention: one claim per node, by design

Three habits keep this error out of your logs:

  1. Set the serials in the launch file on day one, not after the first failure. A saved configuration with both serials makes the mapping permanent.
  2. Give each camera a stable name with a udev rule, so index and plug order stop mattering:
SUBSYSTEM=="video4linux", ATTRS{serial}=="<serial of camera A>", SYMLINK+="depth_cam_a"
SUBSYSTEM=="video4linux", ATTRS{serial}=="<serial of camera B>", SYMLINK+="depth_cam_b"

Your node then opens /dev/depth_cam_a and /dev/depth_cam_b, and a replug cannot swap the cameras. ATTRS{serial} carries the USB serial; a camera without a programmed serial needs a port-based rule instead.

  1. Once the claims are right, plan the streams themselves — trigger mode, synchronization and topic layout are the next exercise. The multi-camera ROS launch planning article starts there.

Order of checks, start to finish

  1. Read the errno: EBUSY means a holder exists, so the format is not the problem.
  2. Run fuser -v /dev/videoN on the exact node from the error.
  3. If the holder is your own first instance, add the per-camera serials to the launch.
  4. Run v4l2-ctl --list-devices and map the sibling nodes before relaunching.
  5. Relaunch and recount: every capture node carries exactly one claim.
  6. Still busy after a clean stop? Reload the module or reboot once, then recount.
  7. Persist the mapping with udev symlinks so the next replug cannot reshuffle the indices.

Where to go next

If the rig behind this error is a depth pair for picking, palletizing or volume measurement, the 3D sensors category is where its cameras come from, and area scan cameras covers the 2D side of a mixed station. The camera finder filters by interface and resolution, and the camera configurator pairs a camera with lens, light and controller in one pass. If the error survives every check above, contact us with the full error text and your launch file — the failure line names the node, and that node is the whole diagnosis.

Cameras3D sensors

Laser profilers and stereo 3D cameras

170 models · from $699.00

Questions

Which command shows the process holding a V4L2 camera node?

Run fuser -v /dev/video0 for the node named in the error; it prints the PIDs, and lsof adds the command names. Check each PID with ps before stopping anything, because the holder is often your own first launch instance or a viewer left open. If both tools show nothing and the node still reports busy, the driver is stale: reload the module or reboot once.

Why do two node instances open the same camera without serials?

A launch instance with no serial set falls back to the first device that matches its filters, and both instances match the same one. The first streams it; the second reaches VIDIOC_S_FMT, finds the node in use, and gets EBUSY. One serial parameter per instance removes the fallback, and each instance then resolves to its own camera's capture node.

Can two programs share one video node at the same time?

Several processes can hold the node open, but only one may stream it: the kernel returns EBUSY to a second streamer at format set or stream start. Sharing means sharing frames in software — one process grabs and passes images over IPC, or a v4l2 loopback device mirrors a processed stream — not two independent captures on one node.

Does Device or resource busy mean the pixel format is wrong?

No. An unsupported format fails with EINVAL, and the node lists its valid choices in v4l2-ctl --list-formats-ext. EBUSY is about access, not format: another handle is streaming or holding the device. Chasing format settings while EBUSY persists wastes the session; fix the claim first and the format error, if any, will name itself.

How does camera order stay stable across reboots and re-plugs?

Write a udev rule that maps each camera's USB serial to a fixed symlink such as SYMLINK+="depth_cam_a", then open that path in the launch instead of /dev/videoN. Enumeration order then cannot swap the cameras. A camera without a programmed serial needs a port-based rule or serial programming through the vendor tool; udevadm info on one node shows whether ID_SERIAL is populated.