System designTroubleshooting

Invalid frame ID in canTransform: frame does not exist

canTransform reports an invalid frame ID — frame does not exist. Find the break with view_frames and join the camera and robot trees with one static edge.

LumaSpec Vision10 min read Share on LinkedIn

On this page 8 sections
  1. 01What the error is actually telling you
  2. 02The two causes, in order of probability
    1. Cause 1: a parameter names a frame nobody publishes
    2. Cause 2: two islands with no bridge
  3. 03Find the break in three commands
  4. 04Worked example: base_link missing on a stereo depth camera rig
  5. 05The fix: rename one parameter or publish one edge
  6. 06When the bridge does not cure it
  7. 07Prevention: one name, one owner, one root
  8. 08Questions

Your depth-camera node starts, video and point clouds flow, and the log fills with Invalid frame ID "base_link" passed to canTransform argument source_frame - frame does not exist. The camera is fine; the transform tree is not. This guide is for engineers bringing a 3D camera wrapper into a ROS 2 stack that already has a robot base frame or a world frame. You will read the error correctly, list the frames the driver really publishes, find the one missing edge, and close the gap with either a parameter change or a single static transform.

What the error is actually telling you

tf2 keeps two buffers, /tf and /tf_static, of named edges between frames. Your node called canTransform with source_frame set to base_link, and the buffer answered: it has never seen a frame with that exact name. Nothing timed out and nothing is corrupted; the string base_link simply has no edges attached to it.

That leaves two causes, and they often arrive together:

  1. The name is unpublished. A frame-name parameter or a hardcoded string says base_link, while the driver publishes frames prefixed with the camera's model name, such as camera_base_link. TF frame names are plain strings: namespaces do not prefix them, and a one-character difference is a different frame.
  2. The tree is split. The camera wrapper publishes its own island, your robot description publishes another, and nobody has published the edge that joins them.

A third failure looks similar but is different: the name exists and the data is missing, which produces an extrapolation error or a lookup timeout instead. If the log says frame does not exist, it is a naming or topology problem — treat it as one.

Log text What tf2 means First move
frame does not exist The name has never been published Fix the name or publish the missing edge
would require extrapolation into the past or into the future Name known, time outside the buffer Look up the latest transform or widen the buffer
Lookup times out, nothing on /tf Publisher dead or domains split Check ros2 node list and the DDS domain

The same split tree also shows up on the display side, as a fixed-frame complaint; the Rviz fixed frame world does not exist guide covers that variant.

The two causes, in order of probability

Cause 1: a parameter names a frame nobody publishes

Every wrapper prints its frame parameters at startup. A typical dump for this failure reads Base frame id: base_link, with Map frame id: map and Odometry frame id: odom beside it, while the state publisher in the same log lists six frames, all prefixed with the camera model: the camera root, a camera-center frame, left and right camera frames, and their optical frames. Plain base_link appears in the parameter file and in no publisher.

Test: put the parameter dump beside the tree output from the next section and compare the strings. Fix: set the parameter to a name the tree actually contains.

Cause 2: two islands with no bridge

The wrapper's launch file started robot_state_publisher with the camera model alone, so the published tree was rooted at the camera-prefixed link. Your robot description, if one runs, is a separate island rooted at base_link or the controller's own root. Until one edge joins them, every lookup that spans the gap fails with frame does not exist — no matter how healthy the camera streams are. The same pattern awaits any camera driver you add later, stereo, ToF or area scan: the driver owns frames named after itself and nothing else.

Find the break in three commands

Run these on the machine running the stack; each takes seconds.

  1. ros2 run tf2_tools view_frames — draws the whole tree. Count the roots: one connected tree has exactly one. Two roots means two islands, and the missing edge is the one between them.
  2. ros2 run tf2_ros tf2_echo base_link camera_base_link — tests one pair by name. If tf2_echo fails on the missing name, you have confirmed the exact edge that breaks.
  3. ros2 topic echo /tf_static --once — prints the latched static edges with their frame names. Copy names from this output into your parameters instead of retyping them.

If view_frames shows one root and your frame is still absent, the publisher that should own it has died or never started: check ros2 node list, and confirm all your nodes share one DDS domain, because a domain split leaves /tf invisible across it.

Inputs, all from one startup log of the failing rig:

  • The failing call: Invalid frame ID "base_link" passed to canTransform argument source_frame - frame does not exist.
  • The frame parameters: Base frame id: base_link, Map frame id: map, Odometry frame id: odom.
  • The published tree, from the state publisher lines: camera root → camera center → left and right camera frames → their optical frames, 6 frames in one island.
  • The query, repeated by the node: source_frame set to base_link.

Rule: tf2 answers a lookup only when both names sit in one connected tree. A static edge, once published, joins two trees permanently; a dynamic edge joins them only while its publisher runs.

Arithmetic:

  • Frames in the published tree: 6.
  • Occurrences of base_link among them: 0.
  • Edges needed to connect base_link to the camera root: 1. Edges published between them: 0.
  • 0 of the 1 required edges exist, so every lookup fails — while the video stream at 60 Hz and the point cloud at 10 Hz keep flowing, because image data and transforms travel on separate pipelines.

Result: the node spams the error at every lookup, from launch, for as long as it runs.

Decision: pick one fix. (a) Set the wrapper's base frame id to the camera root frame: zero new edges, one line changed, and poses are expressed in the camera's own root. (b) Publish exactly one static transform from base_link to the camera root: keeps the parameter, and gives the camera a real pose in the robot or world frame. The threshold either way: view_frames must show a single root afterwards, and every frame must keep exactly one parent — a second parent for the camera root breaks the tree again.

The fix: rename one parameter or publish one edge

Fix A — point the parameter at a frame that exists. Set the base-frame parameter to the camera's root, spelled exactly as view_frames spells it. Choose this on the bench, or whenever the camera does not yet need a pose in the robot frame.

Fix B — publish the missing edge once, as a static transform:

ros2 run tf2_ros static_transform_publisher --x 0.12 --y 0.03 --z 0.05 --yaw 0 --pitch 0 --roll 0 --frame-id base_link --child-frame-id camera_base_link

Those numbers are example mount offsets; measure the real translation and rotation of the mount before the cell ships. In a launch file, publish the same edge from a StaticTransformBroadcaster: one TransformStamped with header.frame_id set to base_link, child_frame_id set to the camera root, and the measured translation and rotation in transform.

When the other island is a robot controller's own frame tree rather than a ROS description, read how that controller nests and names frames before bridging — how child frames follow base changes on a robot controller. When the edge is the calibrated camera-to-base pose, publish it as a static transform the way the eye-to-hand calibration as a static camera transform guide sets out, and keep the reference frame behind a camera pose initial guess straight while you measure it.

When the bridge does not cure it

The bridge is the usual answer. If the error survives it, work down this list:

  • The string still does not match. An underscore where the tree has a hyphen, a prefix missing, a capital where the driver has none. Copy names from view_frames; do not retype them.
  • The publisher died. One root on the old diagram but the camera island missing on the new one: check ros2 node list, restart the state publisher, and only then re-diagnose.
  • You are reading a different error. Extrapolation into the past or future, or a lookup timeout, means the name is known and the time window is wrong. Look up the latest transform, widen the buffer, or fix the timestamp source. Adding transforms to a timing fault creates a second problem.
  • The tree is connected but the data is nonsense. An inverted point cloud or an ROI in the wrong place means the static publisher had frame-id and child-frame-id swapped: the inverted point cloud ROI diagnosis walks that case, and the depth camera IMU and optical frame check verifies the camera-side names.

Prevention: one name, one owner, one root

  • Before launch, list every frame-name parameter — base, map, odometry — and match each against the tree the state publisher will generate. The missing camera frame fix for the frame tree shows the general check when a camera frame never appears.
  • Publish each edge once, from one node. If the robot description already owns the camera-mount edge, delete the extra static publisher instead of adding a competing one.
  • Rerun view_frames after every launch or parameter change. The diagram is the contract between your nodes; a thirty-second check catches a renamed frame before your first lookup does.
  • Do not chase this error with hardware. A marginal connection drops images and point clouds; it never removes a name from /tf. Keep spare camera cables for the dropout faults instead.

For the mechanical half of a new rig — where the camera sits and what it can see from there — the 3D camera working distance and positioning guide covers mounting and field of view, and the 3D sensors finder shortlists depth and profiler cameras when the project outgrows the bench unit.

Questions

Why does Rviz look fine while the lookup fails?

A display can sit inside one island and draw it completely, so the camera frames look present. The failing lookup asks for a name from the other island, and that name has no edges in either buffer. Check which tree the display's fixed frame belongs to before you trust what you see.

Where do the exact frame names come from?

From the driver: the camera-name or prefix parameter decides the strings, the state publisher prints them at startup, and view_frames lists the whole tree. Echo /tf_static once to see the latched names byte for byte. Copy names from those outputs into your parameters; retyping them is how one-character mismatches survive.

Should the bridge transform be static or dynamic?

Static whenever the camera is rigidly mounted: a static edge is published once, latched, and valid at every lookup time. Use a dynamic publisher only when the camera moves relative to its parent, for example a wrist-mounted camera fed by robot joint states. Fixed mounts and bench rigs need exactly one static edge.

How is frame does not exist different from an extrapolation error?

Frame does not exist is a naming failure: no amount of waiting produces the name. An extrapolation error means the name is known but the requested time lies outside the buffered window, which you fix with the lookup time or a larger buffer. The two errors need opposite responses: rename versus retime.

Can two nodes publish the same frame at once?

They can, and the tree then has two parents for one child, which is invalid. Lookups flip between the two poses or fail outright. When your robot description already owns the camera-mount edge, remove the duplicate static publisher instead of adding one, then confirm the single owner with a fresh view_frames run.

The error stops after a few seconds at startup — is that the same fault?

No. A name that resolves after a delay was always published; your lookup simply ran before the first message arrived. Wait for the first transform or retry with a short timeout. True frame-does-not-exist failures never cure themselves, which is the quickest way to tell the two apart.