Skip to main content

baglint

Validates the contents of MCAP recordings against a declarative specification. Checks for missing topics, recording gaps and rate violations, and returns a non-zero exit status when a recording does not satisfy the specification.

Validation of the MCAP container itself, such as chunk CRCs and index integrity, is out of scope. Use mcap doctor for that.

Requirements

  • Python 3.10 or later

Installation

$ python3 -m venv .venv
$ .venv/bin/pip install -e .

When ROS 2 is sourced, /opt/ros/$ROS_DISTRO/lib/python3.*/site-packages is on PYTHONPATH and is inherited by the virtual environment, which causes ROS pytest plugins to load during test runs. Clear it when invoking the tool:

$ env -u PYTHONPATH .venv/bin/baglint --help

Usage

$ baglint BAG [-s SPEC] [-f {text,json}] [--strict]
$ baglint BAG --init [--margin FRACTION]
Option Description
-s, --spec Specification file to validate against
-f, --format Output format, text (default), json or html
--strict Exit non-zero on WARN findings as well as FAIL
--init Print a specification generated from the recording instead of validating it
--margin With --init, the fraction below the observed rate at which to set min_rate. Default 0.1
--version Print version and exit

Without -s, no checks run and nothing is validated.

Generating a specification

--init writes a specification describing a recording, which is the practical way to produce a first one:

$ baglint good_run.mcap --init > spec.yaml
$ baglint run_028.mcap --spec spec.yaml

min_rate is set below each topic's observed mean rate by --margin, and max_gap_ms to twice the worst interval observed. Topics with fewer than ten messages are generated as presence-only, since a mean rate over so few samples describes the recording length rather than the publisher.

The bounds describe the recording they came from, defects included. Generate from a run that is known to be good, and treat the result as a starting point.

Specification

Topic keys accept glob patterns. The first matching entry applies, so list specific topics before wildcards.

topics:
  /joint_states:
    min_rate: 490
    max_gap_ms: 10

  /camera/*:
    min_rate: 25

  /diagnostics:
    required: false
Key Type Description
min_rate float Minimum mean publication rate in Hz, measured over the topic's own span
max_gap_ms float Maximum permitted interval between consecutive messages
required bool Whether a topic named literally must be present. Default true
check_stamps bool Validate header.stamp ordering. Default false, as it deserializes payloads

Transforms

Required frame pairs are checked for availability across the recording:

transforms:
  required:
    - [camera_link, base_link]
  max_stale_ms: 500

Each pair is resolved through the transform tree, in either direction, since a transform can be inverted. /tf_static is treated as valid for the whole recording. A dynamic edge is usable from one update to the next and for max_stale_ms after its last, which defaults to 500. Reading transforms requires deserializing /tf, so it happens only when required is non-empty.

Output

$ baglint run_028.mcap --spec examples/demo_spec.yaml
run_028.mcap
  6 topics · 86,095 messages · 2m00s

FAIL /camera/image
  rate 24.0 Hz below minimum 25 Hz  [log_time]

FAIL /diagnostics
  only 1 message(s); too few to measure a rate against the 1 Hz minimum  [log_time]

FAIL /imu
  1 stamp(s) moved backwards (worst 190.0 ms at 50.00 s)  [header.stamp]

FAIL /joint_states
  2 missing interval(s) >10 ms (worst 112.0 ms at 88.50 s)  [log_time]

FAIL /scan
  required by spec but no messages in bag

FAIL /tf
  camera_link -> base_link unavailable for 13.52 s across 1 interval(s) (worst 13.52 s at 60.48 s)  [header.stamp]

WARN /imu
  18 message(s) repeated the previous stamp  [header.stamp]

WARN /imu
  6 message(s) carried a zero stamp  [header.stamp]

8 findings: 6 FAIL, 2 WARN

To regenerate the recording used above:

$ python examples/make_demo_bag.py run_028.mcap

--format html writes a self-contained report with one track per topic on a shared time axis, showing where each defect falls and whether defects on different topics coincide:

$ baglint run_028.mcap --spec examples/demo_spec.yaml --format html > report.html

baglint HTML report

Grey marks when a topic was recorded, red a failure, amber a warning. A topic struck through was never recorded at all. The report embeds its styles and fetches nothing, so it can be published as a CI artifact.

--format json emits the same findings for machine consumption. Each carries a stable code, intended for filtering and baselining in CI:

Code Level Meaning
gap FAIL Consecutive messages exceeded max_gap_ms
rate_below_min FAIL Mean rate fell below min_rate
rate_unmeasurable FAIL Fewer than two messages, so no rate can be computed
missing_topic FAIL A required topic carried no messages
stamp_backwards FAIL A header.stamp preceded the stamp before it
stamp_duplicate WARN A message repeated the previous header.stamp
stamp_unset WARN A message carried a zero header.stamp
stamp_unavailable WARN check_stamps was set on a message type without a header
tf_unavailable FAIL A required frame pair was unresolvable for some span
tf_frame_missing FAIL A required frame never appeared in the transform tree

Message timestamps

Each message carries two timestamps, and findings state which one was used:

Timestamp Set by A defect indicates
log_time the recorder, on write the recorder stalled: disk I/O, CPU starvation, a terminated node
header.stamp the publisher, at sample time the sensor or its driver misbehaved

Gap and rate checks read log_time. Stamp checks read header.stamp and are enabled per topic with check_stamps, which is off by default because it requires deserializing every message on that topic:

topics:
  /imu:
    check_stamps: true

A stamp that moves backwards is reported as a failure. Downstream consumers such as tf2, message_filters and the SLAM backends assume a non-decreasing stamp sequence per topic, and do not report the violation themselves. Duplicate and zero stamps are reported as warnings.

A zero stamp is treated as never populated rather than as a timestamp, so it does not become the baseline for the messages that follow it.

Exit codes

Code Condition
0 No FAIL findings. With --strict, no WARN findings either
1 At least one finding at or above the failing level
2 Invalid arguments, unreadable recording, or malformed specification

Development

$ make install
$ make test

Tests construct MCAP recordings with injected defects at known offsets. See tests/fixtures.py.

Metadata

Release files for baglint 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for baglint 0.2.0
File Size Uploaded
baglint-0.2.0.tar.gz 270.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for baglint 0.2.0
File Interpreter ABI Platform
baglint-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 296.7 kB

Release files / baglint-0.2.0.tar.gz

Download URL baglint-0.2.0.tar.gz
Size 270.4 kB
Tags Source
SHA-256 checksum
How to use checksums
1b23b46141a7438f4bc6f646e56e59fbbd505432feff58af460f9a9695b73be1
BLAKE2b-256 checksum
How to use checksums
3273e4fa1010b60bd82203876c1009d47bee3a67b384acee72ef0708a938788c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / baglint-0.2.0-py3-none-any.whl

Download URL baglint-0.2.0-py3-none-any.whl
Size 26.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
daf6926489f62f72083b85ddc9e306fd713025dd96db35423c0889bfbaf12386
BLAKE2b-256 checksum
How to use checksums
c6f83c02ff45ecdbeeb2581df180b84c8f5bc2cb4cc19e0bac76516394deaf7b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

0.2.1

2 release files

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page