You installed a camera driver node from the standard ROS 2 repository on a fresh Linux machine, launched it against a known serial number, and it died before the first frame: what(): System instance cannot be acquired, error -1012, and the launch report ending in exit code -6. The same camera runs on the old machine, so the hardware looks guilty. It is not. The node never reached the camera: it could not load the GenTL producer library, because the environment variable pointing at the .cti file is empty. Below is what the crash means, the one export that fixes it, the checks that prove it, and what to do when the export alone is not enough.
What the crash actually says
Work backwards from the exit code. A negative exit code in a ROS 2 launch report means the process was killed by a signal, and the number is the signal: -6 is signal 6, SIGABRT. That is the C++ runtime calling abort() after an exception nobody caught, exactly what terminate called after throwing an instance of ... announces one line earlier.
The exception text carries the diagnosis. The producer could not be loaded, and the message names two identifiers: SPINNAKER_GENTL64_CTI and Spinnaker_GenTL.cti. Error -1012 in this SDK family is the producer-load failure, raised while the node asks for its system instance, the entry object every GenTL session creates first.
The chain is node → GenTL API → producer, a .cti shared library → transport → device. No producer means no system instance, no interfaces and no devices. For the full data path drawn out, read how the depth camera GenTL producer setup works; the architecture is identical for a USB3 area scan camera.
The log proves the hardware is not involved
Pull the timestamps from the log. The node logged its parameter definitions file at 1727677473.953809714 and its calibration URL at 1727677473.960900730:
1727677473.960900730
- 1727677473.953809714
= 0.007091016 s (about 7.1 ms)
The abort lands immediately after the calibration line. Nothing between those lines enumerates an interface or opens a device, because a system instance must exist before either is possible. The camera, the cable, the USB port and the udev permissions are all downstream of this failure. Leave them plugged in and fix the producer path.
Why the variable is empty on this machine
Causes in probability order:
-
The variable is unset. On distributions where the vendor SDK had no supported build at the time, Ubuntu 24.04 being the case here, the driver package installs SDK-less and nothing exports
SPINNAKER_GENTL64_CTIfor you. This is the -1012 crash. - The variable points at a file that moved. An SDK upgrade or removal leaves a stale path; the producer existed once and does not now.
-
The file exists but will not load. Its dependent libraries must resolve first. A known-issues page for another GenTL producer,
mvGenTLProducer.cti, describes software linked against alibusbdifferent from the system one losing its USB3 Vision devices: the producer loads, enumeration comes back empty. - The variable is set in the wrong environment. An export in your interactive shell never reaches a node started by a service, an IDE or a container.
- Permissions. Missing udev rules fail later, not here: the producer loads and the device list comes back empty, the empty camera list: match the CTI case.
The fix, step by step
-
Locate the producer file:
find /usr /opt -name 'Spinnaker_GenTL.cti' 2>/dev/null dpkg -S Spinnaker_GenTL.ctiOn the repository package route under ROS 2 Jazzy it lands at
/opt/ros/jazzy/lib/spinnaker-gentl/Spinnaker_GenTL.cti. -
Confirm it is a real file, not a dangling symlink:
ls -lon the path you found. -
Export it in the shell you launch from:
export SPINNAKER_GENTL64_CTI=/opt/ros/jazzy/lib/spinnaker-gentl/Spinnaker_GenTL.cti -
Relaunch the node from that same shell. Success is the log passing the calibration line and continuing into interface enumeration and publishing, the exact stage the crash used to interrupt.
-
Persist it: append the export to the profile the launching user reads, or set it in the launch description's environment action or the service definition, so the node gets it no matter who starts it. When you set the path from a script, mind the quoting and escaping rules that bite file paths in code; the CTI path escaping errors article shows the failure modes.
-
Write the variable and its value into the station's commissioning record next to the driver and SDK versions.
How to prove the producer loads
Run the two shell checks first:
echo "$SPINNAKER_GENTL64_CTI"
test -f "$SPINNAKER_GENTL64_CTI" && echo producer-file-ok
An empty line from echo means the export never reached this shell; fix that before anything else. A failed test -f means the path is wrong, so return to step 1. Then relaunch the node: getting past calibration into enumeration proves the producer loaded and the system instance was acquired.
For proof outside the node, open the producer with any GenTL-aware tool and list interfaces. If it reports no error, enumerate anyway before trusting it: validate the CTI before you enumerate explains why a silent open is not a pass. Once images flow and your downstream tools still see nothing, find the topic name the node really publishes before touching the driver again.
When the export alone is not enough
- Producer loads, then calls fail with missing functions. The producer generation does not match the node build; the GenTL producer version compatibility: missing function errors guide covers the alignment fix.
- Producer loads, device list empty. Work the CTI-match and permissions path from cause 5, not the variable.
-
USB3 Vision devices disappear while other interfaces stay. That is the
libusblinking conflict from cause 3; make every component on the machine use the systemlibusb. -
The value carries a stray quote or trailing space.
echoshows it wrapped in characters you did not intend; retype the export with the bare absolute path.
Make the fix survive the next reinstall
- Pin the driver and producer to a version pair you have tested, and record both numbers together. Mixed generations are the main source of the follow-on errors; the SDK version pinning fix for edge inference crashes makes the same case for acquisition stacks.
- Keep the export in the launch environment rather than one person's profile, so a service restart after a power cut brings the node up with the variable present.
- Re-run both shell checks after every OS or SDK update on the machine. An update that moves the producer file recreates this crash exactly.
Parts that make USB3 bring-up easier
The fault was software, but USB3 stations behave better with the right parts: a quad-port USB3 adaptor card such as the MV-GU2204 keeps the cameras off the chipset's shared ports, and a drag-chain-rated cable such as the LS-CBL-0219 survives movement that kills standard cords. Browse the camera finder or the area scan cameras category for the camera side.
- Interface
- USB3
- Ports
- 4 ports
For a new station camera, a USB3 model such as the LS-CMR-0166 runs on this same producer path.
- Sensor format
- 1/1.7"
- Interface
- USB3
- Resolution
- 12 MP
- Shutter
- Rolling
Checklist before you close the ticket
-
echo "$SPINNAKER_GENTL64_CTI"prints the full path in the environment that launches the node. -
test -fon that path succeeds. - The node log passes the calibration line and reaches interface enumeration.
- The first image topic appears, not just a clean start.
- The export lives in the launch environment or service definition, not only in one shell's history.
- Driver, SDK and producer versions are recorded together as a tested pair.
If the producer loads and the fault moves deeper into acquisition, triggering, timestamps or multi-camera sync, the applications team can work the log with you through the contact page.
Questions
Does setting SPINNAKER_GENTL64_CTI need a reboot?
No. The variable is read when the process starts, so relaunch the node from the updated shell and the new value applies. Processes already running keep the environment they were started with, which is why an export sometimes appears not to work until the node is actually restarted. If a service starts the node, restart that service after setting the variable where the service reads it.
Would a GigE camera avoid this crash?
No. The system instance is created before any transport is chosen, so the crash is transport-blind: a USB3 Vision camera and a GigE Vision camera both fail identically when the .cti path is empty. Transport only matters after the producer loads, at interface enumeration. Choose the camera by bandwidth, cable length and frame rate, not to dodge this error.
Can the variable be set in the launch description instead of the shell?
Yes, and on shared machines it is the better place. Launch tools can define environment variables for the processes they start, which survives operator changes and machine reboots. Use the bare absolute path as the value. Keep one source of truth: if a shell profile, the launch description and a service file all set it, the value the node actually sees is the last one written into its environment, and a typo in any of them becomes hard to find.
Why do quotes around the path cause this same error?
Some env-file formats and configuration parsers keep quotes as part of the value, so the SDK looks for a file whose name literally contains quote characters and fails to load it as a producer. A trailing space does the same. Echo the variable between delimiters, for example echo "[$SPINNAKER_GENTL64_CTI]", and any stray character shows up immediately against the brackets.
If the full vendor SDK is installed later, does the variable need to change?
Point it at the one producer that matches the driver build and leave it there. Two SDK generations on one machine is the classic setup for the next failure in the sequence: the producer loads but calls fail with missing functions. After any SDK change, re-run the shell checks and confirm the node still reaches interface enumeration before you close the ticket.


