System designTroubleshooting

Output queue stream name doesn't exist: build the pipeline first

Your output queue fails because the stream name doesn't exist at boot: the device built an empty pipeline. Build the graph, boot, then read by the exact name.

LumaSpec Vision7 min read Share on LinkedIn

On this page 8 sections
  1. 01What the exception actually reports
  2. 02Why the stream table freezes at boot
  3. 03Rank the causes, then test each
  4. 04Fix: build the graph, then boot the device
  5. 05Does the stream fit the link?
  6. 06Where the host code runs
  7. 07Prevent it: the boot-order rules
  8. 08Questions

Your code opens the camera, adds a color node, and the first getOutputQueue call throws std::runtime_error: Queue for stream name 'camera' doesn't exist. The device opened without complaint and the node exists in your source, so the message looks impossible. It is exact: the camera runs no output stream by that name, because it booted from an empty graph. Here you get the four causes ranked with a test for each, the corrected construction order, the proof test, and the bandwidth arithmetic that decides whether the stream fits the link.

What the exception actually reports

getOutputQueue(name, maxSize, blocking) is a lookup, not a request. It searches the output streams the device registered when it was constructed and throws the moment the string misses. The exception therefore carries one fact: at boot time, no output stream with that name existed on this device.

There are two ways to miss. The stream never existed at boot because the graph was empty or incomplete, or it exists under a different string because setStreamName received one literal and getOutputQueue another. Test the boot order first: it is the cause whenever a start() function opens the device and node constructors run afterwards, typically in a wrapper class. The bug reduces to three lines:

auto pipeline = std::make_unique<dai::Pipeline>();                              // empty blueprint
auto device   = std::make_unique<dai::Device>(*pipeline, dai::UsbSpeed::HIGH);  // boots with zero streams
// later, in a camera wrapper's constructor:
auto cam = pipeline->create<dai::node::ColorCamera>();  // host-side only; the running device never sees it

The class, node and method names are the SDK's; the variable names are my own illustration. If your failure happens earlier, at open time with a different message, work through camera detected but cannot be opened: fix the capture pipeline instead.

Why the stream table freezes at boot

A pipeline is a build description, not a connection. Constructing the Device serializes the graph, uploads it and starts the camera firmware against that copy. The XLinkOut nodes present at that instant are the output streams the host can ever open; node creation afterwards edits a host-side object the firmware does not watch, so a ColorCamera and XLinkOut added a line later never reach the camera.

Wrapper classes add a second trap: lazy construction. A constructor that builds a fresh pipeline when its pointer is null leaves two graphs in one process, and the device stays bound to the first, empty one. One session means one pipeline and one device built from it.

Rank the causes, then test each

Work down the table; the first three decide most cases.

Rank Cause Test Fix
1 Device constructed from an empty pipeline In the start-up path, is the Device line above the first create<...>() call? Move every node creation and link call above it
2 Nodes built on a second pipeline object Search the project for pipeline constructions; there must be one per session One owner builds the graph and passes it to the rest
3 Stream-name mismatch Log the string at setStreamName and at getOutputQueue and compare them exactly One named constant feeds both calls
4 Queue opened on the wrong device instance Count Device constructions; more than one per camera is the fault Reopen queues from the device that booted your pipeline

Cause 4 has its own signatures: a second Device on one camera either fails to open or holds a different graph. The device already in use error and the disappearing device nodes walkthroughs cover both sides of that fault.

Fix: build the graph, then boot the device

  1. Construct one pipeline for the session and keep it alive as long as the device runs.
  2. Create every node on that object: the ColorCamera with its preview size, board socket, resolution and color order, plus one XLinkOut per stream you will read.
  3. Link each source to its sink and set each stream name exactly once.
  4. Construct the Device from the finished pipeline; this line writes the stream table.
  5. Open the queue with the same name constant and run the display loop.
// Build phase: SDK names are documented; width, height and streamName are my values
const int width = 640;
const int height = 640;
const std::string streamName = "preview";

dai::Pipeline pipeline;
auto cam = pipeline.create<dai::node::ColorCamera>();
cam->setPreviewSize(width, height);
cam->setBoardSocket(dai::CameraBoardSocket::CAM_A);
cam->setResolution(dai::ColorCameraProperties::SensorResolution::THE_1080_P);
cam->setInterleaved(true);
cam->setColorOrder(dai::ColorCameraProperties::ColorOrder::RGB);

auto camOut = pipeline.create<dai::node::XLinkOut>();
camOut->setStreamName(streamName);
cam->preview.link(camOut->input);

// Boot phase: only now, from the finished graph
dai::Device device(pipeline, dai::UsbSpeed::HIGH);

// Read phase: the exact same constant
auto previewQueue = device.getOutputQueue(streamName, 8, false);
cv::Mat frame = previewQueue->get<dai::ImgFrame>()->getCvFrame();
cv::imshow(streamName, frame);

When the first read after a clean boot fails on the transport side instead, the first GenTL read that always fails walkthrough is the adjacent case.

After the queue exists, the next failure is a link that cannot carry the frames. The constructor argument in the failing code caps the camera to USB high speed, and the cap has consequences you can compute:

frame bytes = width × height × 3 (8-bit RGB, interleaved) link MB/s = frame bytes × frame rate ÷ 1,000,000

Worked example, the preview above: 640 × 640 × 3 = 1,228,800 bytes, about 1.23 MB per frame. At 30 frames per second that is 1.23 × 30 ≈ 37 MB/s. A high-speed USB 2 link is rated 480 Mbit/s, 60 MB/s on paper and roughly 35-40 MB/s in practice after protocol overhead, so one small preview fits with little margin. The full sensor stream does not: 1920 × 1080 × 3 = 6,220,800 bytes, which at 30 frames per second is 186.6 MB/s, roughly half the around-400 MB/s a USB 3 link moves in practice.

Decision: keep the high-speed cap only while you ship a single reduced-size preview; request USB 3 speed for any full-resolution stream, and confirm the host exposes a matching port.

Where the host code runs

The pipeline runs on the camera; the SDK runtime, OpenCV, your display loop and, later, a model all run on the host. The numbers above size it: one USB 3-class port for the 37 MB/s preview link and any future full-resolution stream, one core for the display loop, and about 10 MB of queue memory (8 frames × 1.23 MB). On a line, that host is a vision controller, a fanless computer with camera-oriented ports.

The MV-VC3301H-128G66 carries 3-4 USB 3 ports, 8 GB of memory and an Intel Core i3 processor, which covers this preview workload with room for the SDK and tooling.

Vision controllersHikrobot MV-VC3301H-128G66 I3-8100, 8GB+128G SSD, No PoE, PCIe expansion supported, Incl. VM6200Pro licenseMV-VC3301H-128G66
LAN ports
5+ LAN ports
USB 3 ports
3-4 USB 3 ports
Light outputs
4 built-in channels
Memory
8 GB
$2,419.00View partBuild a system with it

When the same host will also take GigE cameras on a line, PoE camera ports remove the separate switch:

Vision controllersHikrobot MV-VC5525-256G70-NN VC5000 Vision Controller, 12th-I5, 32GB+256G SSD, Supported PCIEMV-VC5525-256G70-NN
LAN ports
5+ LAN ports
USB 3 ports
3-4 USB 3 ports
$4,479.00 · quotedView and quote
Vision controllersHikrobot MV-VC4712-256G10-2TH Ultra 7 255H, 16G×2+256GSSD+2THDD, PCIe expansion, Without VM license keyMV-VC4712-256G10-2TH
LAN ports
3-4 LAN ports
USB 3 ports
2 USB 3 ports
$6,049.00 · quotedView and quote

Prevent it: the boot-order rules

  • One pipeline per session, built by one owner. A second graph guarantees a stale device.
  • Every create<...>() and link() call sits above the Device constructor line.
  • One string constant per XLinkOut; getOutputQueue reads that constant, never a literal.
  • In wrapper classes, track a flag (for example nodesBuilt, initialized false) that flips to true after the first link, and refuse to construct the Device while it is still false.
  • After any graph change, build the new pipeline in full and construct a new Device from it; queues belong to the device that booted the graph.
  • Start every run from the same known state, the discipline behind the flaky test caused by leftover settings walkthrough.

If you are provisioning the machine this code will land on, the configurator below steps through the host choices, and contact is the route when a build needs a quotation.

Build the whole station

The configurator matches a camera, lens, light, light controller and cables to each other, step by step, and lists only parts that fit what you already picked.

Start with the PC

The systems topic page collects the other host-side failure walkthroughs.

Questions

Does 'Queue for stream name doesn't exist' always mean the name is wrong?

No. The lookup fails whenever the device holds no output stream under that exact string, and the most common reason is boot order: the Device was constructed before the XLinkOut existed. A typo or a case difference produces the identical exception. Make one named constant the single source of the string and pass it to both setStreamName and getOutputQueue, and the naming cause disappears by construction.

Can I add a node to the pipeline while the device is running?

No. The graph is compiled and uploaded once, when the Device is constructed, and the firmware runs that copy; later node creation only edits the host-side object. To change the graph, build the new pipeline in full, construct a new Device from it, and open your queues from that new device.

What queue size and blocking flag should I pass to getOutputQueue?

Size the queue in frames. At 30 frames per second, eight frames buffer 8 ÷ 30 ≈ 267 ms, enough to absorb scheduler jitter in a display loop without visible latency. The blocking flag sets behavior under overload: blocking stalls the producer when the queue is full, non-blocking keeps your loop moving at the cost of dropped frames. For a preview window, eight frames and non-blocking is a sound default.

The project compiles and links cleanly, so why does the runtime throw?

Compiling and linking prove the SDK symbols exist, nothing about run order. This exception is thrown at runtime, when getOutputQueue searches the stream table written at boot and finds no match. Errors in find_package, CMake or the linker belong to the build-configuration family and fail before any device opens.

Does the same fix apply to the Python side of the SDK?

Yes. A pipeline-based camera SDK exposes the same architecture in every binding: create the nodes on a pipeline object, link them, then construct the device. The message wording differs per language, but the boot-frozen stream table and the exact-name lookup behave identically, so build, boot, read is the portable rule.