Zephyr zbus Observer Types: Choose Between Latest State and Every Published Message
Choose a zbus observer by what must survive between publication and consumption. A listener executes during dispatch. A subscriber receives a channel reference and later reads that channel’s current value. A message subscriber retains published message data for later consumption. These are different delivery contracts; increasing a subscriber’s notification queue does not turn channel references into historical snapshots.
This guide pins upstream Zephyr 3.7.0 with CONFIG_ZBUS=y, static enabled observers and, where used, CONFIG_ZBUS_MSG_SUBSCRIBER=y. This scope uses CONFIG_ZBUS_ASSERT_MOCK=n; runtime observer changes are excluded. Priority-boost configuration affects scheduling and must be recorded in qualification, but does not change the fundamental reference-versus-message distinction. No target zbus test or throughput benchmark was executed.
Follow the data that each observer receives
The 3.7.0 zbus guide identifies three observer types. A listener is a callback invoked by the dispatcher. A subscriber uses a message queue containing channel references. A message subscriber uses a FIFO of retained message buffers. The reference is an instruction to inspect a channel, not a frozen copy of the value that originally triggered the notification.
Suppose a hypothetical temperature channel publishes sequence 10 with value A and then sequence 11 with value B before a subscriber runs. Two queued references can both lead to reads of B. If the consumer needs current state, that may be acceptable. If it must process both samples, the reference-subscriber contract is insufficient even when no notification was dropped.

Use listeners only for suitable synchronous work
Listeners run in the publishing context while the channel is locked. The tagged dispatcher calls them directly. A slow listener therefore extends publication and delays later observers in that dispatch. A timeout argument cannot preempt an arbitrary application callback that runs too long.
Use the channel’s const-message access intended for listener context rather than trying to lock and read the same channel again. Do not retain that pointer for deferred processing after the callback returns; a later publication can replace its contents. Copy the needed data into storage with a separately established lifetime if work must continue asynchronously.
Audit the publisher contexts before approving a listener. A callback reached from an ISR publication inherits those restrictions; a thread-only callback is not safe merely because most tests publish from a thread. Keep listener work short and bounded, and review any operation that could publish recursively or acquire another channel in an inconsistent order.
Choose reference subscribers for state observation
A normal subscriber waits for a channel reference, then reads the channel through the appropriate API. Provision its notification queue for the expected burst, but keep queue occupancy separate from message history. Several references may describe changes to the same channel, and later reads can observe the same latest value repeatedly.
Put a sequence or version field in the application message when duplicate latest-state observations matter. It lets the consumer distinguish a new state from another notification of an already observed state. Do not equate the number of dequeued references with the number of distinct measurements processed.
Qualify message subscribers against finite resources
Message subscribers retain publication data using network-buffer infrastructure. The release Kconfig provides static or dynamic payload allocation, a network-buffer pool count and optional pool isolation. These choices are part of the delivery design, not incidental memory tuning. A slow observer holds resources while later publications need more.
The tagged observer API provides zbus_sub_wait_msg() for retrieving a message subscriber’s data. Supply a destination large enough for every channel that observer can receive; the function has no destination-capacity argument. A copied message containing pointers is still only a copy of those pointer values, not an independent deep copy of their referenced storage.
A message subscriber is therefore appropriate for preserving individual publication values only when the system also budgets buffer availability and consumption progress. It does not provide unbounded durable storage or automatic exactly-once application processing. Include sequence checks and an explicit policy for publication failure.
Do not assume publication failure rolls everything back
In 3.7.0, publication copies the new value into the channel before dispatching observers. Reference-notification queue operations can fail. A message-subscriber clone allocation can return -ENOMEM, potentially after earlier observers were notified. The initial dispatch-buffer allocation has an assertion path on failure in this release, rather than an ordinary recoverable result that applications can simply retry. The test-only CONFIG_ZBUS_ASSERT_MOCK option instead makes that assertion return -EFAULT; it is outside this scope. Disabling ordinary assertions does not create a safe recoverable allocation-failure path.
Consequently, inspect the exact build’s assertion and resource behavior before claiming lossless delivery. Preventing pool exhaustion, using appropriately isolated pools or adopting a different reviewed implementation may be necessary for the product’s failure requirements. Never present a nonzero publish result as proof that no observer saw the message. Blind retry can duplicate an action for observers that already received it.
A three-observer qualification procedure
- Attach one listener, one reference subscriber and one message subscriber to a test channel. Record sequence identifiers and publication results.
- Hold the two subscriber threads while publishing A then B. Keep the listener bounded and record its observations without blocking logging.
- Release the reference subscriber and demonstrate that two references can yield the latest B value twice.
- Release the message subscriber and verify A then B when both dispatches succeeded and sufficient buffers were available.
- Constrain notification capacity and message-buffer capacity separately. Verify the configured failure behavior, partial delivery evidence and retry policy.
- Repeat with each actual publishing context and the production priority-boost setting. Measure latency only on the target build.
The final choice should name what must survive a delayed consumer: the fact that a channel changed, the current state, or each individual value. Then document callback work, notification depth, buffer budget and failure handling. Observer type is the first decision; resource and execution-context qualification completes the delivery contract.