stalwartlabs/stalwart · error
Cluster::PublisherError
Error message
Cluster::PublisherError
What it means
The NATS pub-sub backend wraps a failed `client.publish(topic, message)` in a trc::Error tagged Cluster::PublisherError. It means the NATS client could not enqueue or send the message onto the given subject. The underlying async-nats error is attached via `.reason(err)`.
Source
Thrown at crates/coordinator/src/backend/nats/pubsub.rs:21
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::NatsPubSub;
use crate::{Msg, PubSubStream};
use futures::StreamExt;
use trc::{ClusterEvent, Error, EventType};
pub struct NatsPubSubStream {
subs: async_nats::Subscriber,
}
impl NatsPubSub {
pub async fn publish(&self, topic: &'static str, message: Vec<u8>) -> trc::Result<()> {
self.client
.publish(topic, message.into())
.await
.map_err(|err| Error::new(EventType::Cluster(ClusterEvent::PublisherError)).reason(err))
}
pub async fn subscribe(&self, topic: &'static str) -> trc::Result<PubSubStream> {
self.client
.subscribe(topic)
.await
.map(|subs| PubSubStream::Nats(NatsPubSubStream { subs }))
.map_err(|err| {
Error::new(EventType::Cluster(ClusterEvent::SubscriberError)).reason(err)
})
}
}
impl NatsPubSubStream {
pub async fn next(&mut self) -> Option<Msg> {
self.subs.next().await.map(Msg::Nats)
}
}View on GitHub (pinned to e962003857)
Solutions
- Read the `.reason` on the trc::Error for the exact async-nats failure (e.g. 'connection closed', 'server disconnected').
- Verify the NATS server URL and that the server is running and reachable from this node.
- Re-establish the connection / retry publishing; use async-nats reconnect options with retry buffers for transient drops.
- Validate the subject string (no spaces; wildcards '*' and '>' are only valid in subscriptions, not always in publishes).
Example fix
// before
let client = async_nats::connect("nats://127.0.0.1").await?;
// after: retry/reconnect handling
let client = async_nats::ConnectOptions::new()
.retry_on_initial_connect()
.connect("nats://127.0.0.1:4222").await?;
client.flush().await?; // detect dead connection early Defensive patterns
Strategy: retry
Validate before calling
// probe connection before publishing
async fn nats_ready(client: &async_nats::Client) -> bool {
client.publisher().is_none() == false && client.flush().await.is_ok()
} Try / catch
for attempt in 1..=3 {
match nats.publish(subject, payload.clone()).await {
Ok(()) => break,
Err(e) if attempt < 3 => {
tracing::warn!(reason = ?e.reason(), "nats publish failed, retrying");
tokio::time::sleep(Duration::from_millis(100 * 2u64.pow(attempt))).await;
}
Err(e) => return Err(e),
}
} Prevention
- Build the client with ConnectOptions::retry_on_initial_connect and generous reconnect buffers.
- Call flush() periodically to detect dead connections early.
- Monitor NATS server health and alert on disconnect events.
When it happens
Trigger: Calling `NatsPubSub::publish(topic, message)` when the NATS connection is closed/disconnected, the client's publish future fails (connection dropped, buffer flush failure, or an invalid subject string).
Common situations: NATS server restarted or unreachable between connect and publish; publish called on a client whose connection was closed after a disconnect; subject names with illegal characters (spaces, wildcards in publish subjects).
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- Cluster::PublisherError
- Cluster::SubscriberError
- Cluster::PublisherError
- unwrap_tls called on non-TLS acceptor
- StoreEvent::HttpStoreError
AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06).
Data as JSON: /api/errors/dd4dfbaf6cb3c633.
Report an issue: GitHub.