System designTroubleshooting

Single frame acquisition still busy: why repeated starts need a stop

A second GenICam SingleFrame start fails with a busy acquisition source; the fix is a stop per frame, or continuous mode paced by a software trigger.

LumaSpec Vision11 min read Share on LinkedIn

On this page 10 sections
  1. 01Why does the second single-frame acquisition fail with a busy error?
  2. 02What does the GenTL busy error message actually tell you?
  3. 03Is AcquisitionStop really optional in SingleFrame mode?
  4. 04How do you grab one frame at a time without the busy error?
  5. 05When is continuous mode with a software trigger the better pattern?
  6. 06What about hardware-triggered cameras that stay in SingleFrame mode?
  7. 07When must you call AcquisitionStop even in a continuous session?
  8. 08How do you keep starts and stops paired for good?
  9. 09Which pattern should your station run?
  10. 10Questions

Your script sets AcquisitionMode to SingleFrame, fetches one clean frame, calls start a second time — and the GenTL producer answers with a busy error instead of another image. Nothing about the camera changed; the session opened by the first start is simply still open. This guide shows why the single-frame path behaves that way, the stop-and-restart pairing that fixes it in three lines, and how to decide between per-shot start/stop and a software trigger in Continuous mode. The same logic tells you when a stop is mandatory even in a continuous session: before any change that reallocates buffers, such as a new pixel format or ROI. The examples use Python with the Harvesters GenTL consumer; every node name is a GenICam standard feature, so the pattern ports directly to a C++ or C# client.

Why does the second single-frame acquisition fail with a busy error?

Because AcquisitionStart opens an acquisition session and only AcquisitionStop closes it: in SingleFrame mode the camera delivers one frame, but the session stays open, so the second start lands on a source that is still acquiring.

AcquisitionStart does not fire an exposure; it opens a session on the GenTL stream. In SingleFrame mode the device drops exactly one frame into that session and waits. Fetching the buffer hands the image to your code and returns the buffer to the free queue — but the session itself is still marked as running. The next start therefore lands on a source that is already acquiring, and the producer refuses the call.

The failure always shows up at the start, never at the fetch: the first frame was already delivered and processed. The sequence to picture:

  1. AcquisitionStart opens the session; the source is now in its acquiring state.
  2. One frame lands in a buffer.
  3. fetch_buffer() gives you the payload and, through its context manager, returns the buffer to the free queue.
  4. No AcquisitionStop follows, so the session stays open.
  5. The second start hits the busy source and raises.

The GenTL standard puts buffer allocation and announcement before the start of the acquisition chain, so a re-arm reuses the same buffers — starting again is not allocation work, it is a state conflict.

You will meet this on any camera you drive through a GenTL producer: area scan cameras over GigE or USB3, CoaXPress cameras behind frame grabbers. Smart cameras and vision sensors hide the session inside their own runtime, which is why the raw error is mostly a scripting problem.

What does the GenTL busy error message actually tell you?

It tells you the state machine, not the cable: 'The acquisition process is running now' is the source saying the session is still open, so the fault to fix is your start-and-stop pairing, not a connection, a cable or a camera setting.

The line to expect from a producer looks like this (one producer's wording, with its own identifier):

GenTL exception: Operation cannot be performed. Responsible module is busy. (Message from the source: The acquisition process is running now.) (ID: -1022)

Two details matter there. The message text is the diagnosis — the source itself names the running acquisition. The numeric ID is not portable: another producer reports the same condition under a different number, so match on the words, not the code.

Work down the causes in this order:

  1. A missing stop between starts. The default case. Test: add stop_acquisition() after the fetch; if the error disappears, you are done.
  2. An exception that skipped an earlier stop. A crash inside your processing leaves the previous session open. Test: restart the script from scratch; if the first start now succeeds, an earlier iteration leaked its session. Fix: put the stop in a finally block, where an exception inside your processing cannot leave the session open.
  3. A second client holding the source. Another process or thread that opened the same camera refuses your start from its side. Test: close every other client and retry; the one-camera-per-process rule is in the pairing section below.

Is AcquisitionStop really optional in SingleFrame mode?

The SFNC wording covers the device side: after one exposure the camera may consider its job done, so ending frame delivery does not strictly need a stop. Your GenTL producer adds a second state machine — the session on the stream — and that one resets on AcquisitionStop alone. Optional at the device, mandatory at the transport.

So the engineering answer is short: treat the stop as required in code. The call costs almost nothing, and it keeps one script portable across every camera and producer you plug in. A few producers do reset the stream state by themselves after the single frame; you can lean on one if you find it, but write the paired-stop version anyway, because the next camera on the bench may not behave that way.

How do you grab one frame at a time without the busy error?

Pair every start with a stop: open the session, fetch the buffer inside its context manager, call stop immediately, and repeat; the sequence start–fetch–stop takes a few lines and works for any number of single shots.

h is a Harvester instance and ia the ImageAcquirer its create_image_acquirer() call returns; start_acquisition, fetch_buffer and stop_acquisition are Harvesters methods, while AcquisitionMode and its SingleFrame value are GenICam standard names that any compliant camera exposes.

from harvesters.core import Harvester

h = Harvester()
h.add_file('path/to/your_genicam_producer.cti')
h.update()
ia = h.create_image_acquirer(0)                # ImageAcquirer for camera 0
ia.remote_device.node_map.get_node('AcquisitionMode').value = 'SingleFrame'

for _ in range(10):                            # ten single shots, ten sessions
    ia.start_acquisition()                     # opens the acquisition session
    with ia.fetch_buffer() as buffer:          # buffer returns to the queue on exit
        image = buffer.payload                 # your processing goes here
    ia.stop_acquisition()                      # closes the session again

Run it as written and all ten starts succeed. Delete the stop_acquisition line and the second iteration raises the busy exception — the whole bug in one line. That doubles as your regression test for every camera you add.

The same pairing runs unchanged on GigE and on USB3 hardware, because both expose the identical GenICam features through their producers. Two cameras from the shop that this pattern drives:

Area scan camerasHikrobot MV-CU020-90GC 2.5 MP Area Scan Camera, GigE, GMAX4002, ColorMV-CU020-90GC
Sensor format
1/1.7"
Lens mount
C-mount
$309.00View part
Area scan camerasHikrobot MV-CT0MU0060/R/NN/NN 6 MP Area Scan Camera, 3rd Gen., USB3.0, Mono, BASEMV-CT0MU0060/R/NN/NN
Sensor format
1/1.8"
Lens mount
C-mount
$969.00View part

If the frame never arrives at all, that is a different failure; the timeout path in Frame didn't arrive within timeout: one wait call per frame covers it.

When is continuous mode with a software trigger the better pattern?

At shot rates above a few per second, or when your cycle time varies, switch the camera to Continuous mode and fire one software trigger per shot: one session for the whole shift, no re-arm between frames, and no busy error because there is only one start.

The per-shot pairing wins on simplicity and failure isolation: every frame is its own session, so a bad shot cannot poison the next. The continuous session wins on overhead, because there is nothing to re-arm between parts. Set AcquisitionMode to Continuous, TriggerMode to On and TriggerSource to Software; one TriggerSoftware command then delivers exactly one frame into the open session. The session starts once at shift begin and stops at shutdown.

Measure the trade once and decide with one number. Time 100 start–fetch–stop cycles and divide by 100: with example numbers, a timer that shows 0.4 s means the re-arm costs 4 ms per shot. At a 600 ms cycle time the overhead is 4 / 600 = 0.7%, and the simple pairing stays. At 100 ms per part it is 4 / 100 = 4%, and the continuous session pays for itself immediately.

overhead = t_rearm / cycle_time keep the per-shot pairing while overhead < 0.01

The 1% line is a rule of thumb: below it, no station notices the re-arm; above it, the cycle pays for nothing. If pacing does not matter at all, skip the trigger and let the camera free-run in Continuous, fetching the frames you want. A paced one-shot is the Software triggered image capture from a GUI button case, and a session that runs all day is Continuous camera acquisition mode for live image loops; more acquisition guides sit on the systems topic page.

What about hardware-triggered cameras that stay in SingleFrame mode?

Some cameras arm one exposure per hardware trigger from a single session: start once, let every trigger edge drop a frame into the buffer queue, and stop once after the last part; the API then adds no overhead between frames.

With the session opened once, nothing in the API stands between your trigger and the next frame; the ceiling is the camera's trigger-to-frame response and its maximum frame rate, which on global-shutter sensors reaches hundreds of frames per second and beyond. One AcquisitionStop after the last part disarms the camera cleanly.

Arm-per-trigger behaviour is not universal, though: other cameras treat SingleFrame strictly and deliver exactly one frame per start, whatever the trigger does. If your trigger fires and the second frame never comes, move that camera to Continuous with TriggerSource on the hardware line, which is the standard way to run one frame per trigger. The failure signatures are worked through in Hardware trigger stops after one frame: check acquisition mode and GigE camera ignores external trigger: check acquisition mode first.

Wiring the trigger through the camera's GPIO needs an I/O breakout or trigger cable; the configurator starts exactly at that step:

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 trigger sensor

When must you call AcquisitionStop even in a continuous session?

Stop whenever buffers have to be reallocated: a change of image size or pixel format, a switch between mono and colour payloads, or a shutdown that must hand the camera to another process — announcement and allocation of buffers happen before the start, not while the stream runs.

Change the payload geometry under a running session and the producer holds buffers of the wrong size, so the order is always stop, change, start. Most cameras also flip exposure, ROI and pixel-format nodes to read-only while a session is open, and the node map tells you so the moment you try.

Then there is the buffer that never came back. Calling stop while a buffer is still checked out is the classic hang at shutdown, and it usually traces back to a fetch whose with block was skipped or a buffer stored outside its context manager. The recovery paths are worked through in Stop camera acquisition with busy buffers.

How do you keep starts and stops paired for good?

Treat the session as a resource with a destructor mindset: wrap start and stop in try/finally or a context manager, log every start and stop in the run log, and let any unhandled exception shut the session down before the code retries.

Five practices cover nearly every recurrence:

  • One owner per camera. A second process that opens the same source produces the identical busy refusal from the other side; the rule is one camera per process, as laid out in Multiple vision software instances: one camera per process.
  • try/finally around every session. The stop goes where the exception cannot skip it.
  • Log every start and stop with timestamps. When the counts drift apart, the log points straight at the iteration that leaked.
  • Match GenTL errors on message text. The numeric ID changes between producers; the words do not.
  • Recreate the acquirer after a crash. Reopening the device resets the session bookkeeping; trusting the producer's state after an unhandled exception does not.

Producer behaviour differs in details beyond these five; the checklist in Camera acquisition backend compatibility troubleshooting catches the rest before it reaches the cell.

Which pattern should your station run?

Match the pattern to the pacing: a one-shot every few seconds takes SingleFrame with a paired stop; a paced shot per part takes Continuous with a software trigger; a hardware-triggered station starts once and stops once; every pattern stops before changing ROI or pixel format.

Station AcquisitionMode Session pattern When you call stop
Manual one-shot, maintenance capture SingleFrame start → fetch → stop, per shot after every shot
One paced shot per part Continuous, software trigger one session, one TriggerSoftware per part at shutdown, and before ROI or pixel-format changes
One frame per hardware trigger SingleFrame (arm per trigger) or Continuous, hardware line one session, frames arrive on trigger edges after the last part
Live preview or image loop Continuous, free-run one session, fetch as needed when the loop closes

If you are choosing the camera this script will drive, the camera finder filters by interface, shutter and resolution, and the configurator walks the whole chain from the camera step. Where a producer refuses to behave as described, the contact page reaches the applications team.

Questions

Why does my camera work in a viewer tool but fail with the same settings in my script?

Most viewer tools pair AcquisitionStart and AcquisitionStop behind their snapshot buttons, so every capture is its own closed session. Your script calls start twice without a stop in between, and the producer refuses the second one. Add a stop after every fetched frame and the script behaves like the viewer.

Does the numeric ID in the GenTL error matter?

No. The number is the producer's own identifier, and the same busy condition reports a different ID from a different producer. Match on the message text — a source that says the acquisition process is running now is asking for a stop — and log the text so recurrences stay visible in the run history.

What is the difference between SingleFrame, MultiFrame and Continuous mode?

SingleFrame delivers one frame per AcquisitionStart. MultiFrame delivers a fixed batch per start, with the count set by the AcquisitionFrameCount node. Continuous delivers frames until you stop. If your camera offers MultiFrame and you need a burst from one start, it removes the per-frame pairing entirely.

Can I leave a continuous acquisition running for a whole shift?

Yes, that is the normal design for paced stations: one session, buffers cycling through the free queue as you fetch and return them. Two disciplines keep it stable: return every buffer promptly so the queue never runs dry, and stop the session before you change ROI or pixel format, because those changes reallocate buffers.

Is the busy state a camera fault or a library fault?

Neither. The camera may have finished its single frame correctly; the GenTL producer in front of it still counts the session as running until a stop arrives. That split is why the fix lives in your start and stop pairing rather than in a camera register, a cable or a firmware update.