linera-io/linera-protocol · error
a running notification server
Error message
a running notification server
What it means
start_notification_server (linera-exporter/src/exporter_service.rs) serves the gRPC NotifierServiceServer on the configured endpoint with serve_with_shutdown and expects success. The expect fires when tonic fails to serve the endpoint - overwhelmingly a bind failure such as 'address already in use', plus invalid endpoint addresses or socket permission errors. This runs during exporter startup, so the process aborts before serving notifications.
Source
Thrown at linera-exporter/src/exporter_service.rs:96
cancellation_token: CancellationToken,
) -> core::result::Result<(), ExporterError> {
let endpoint = get_address(port);
info!(
"Starting linera_exporter_service on endpoint = {}",
endpoint
);
let (health_reporter, health_service) = tonic_health::server::health_reporter();
health_reporter
.set_serving::<NotifierServiceServer<Self>>()
.await;
Server::builder()
.add_service(health_service)
.add_service(NotifierServiceServer::new(self))
.serve_with_shutdown(endpoint, cancellation_token.cancelled_owned())
.await
.expect("a running notification server");
Ok(())
}
}
fn parse_notification(notification: Notification) -> core::result::Result<BlockId, ExporterError> {
let chain_id = notification
.chain_id
.ok_or(BadNotificationKind::InvalidChainId { inner: None })?
.try_into()
.map_err(|err| BadNotificationKind::InvalidChainId { inner: Some(err) })?;
let reason = bincode::deserialize::<Reason>(¬ification.reason)
.map_err(|err| BadNotificationKind::InvalidReason { inner: Some(err) })?;
if let Reason::NewBlock { height, hash } = reason {
return Ok(BlockId::new(chain_id, hash, height));
}View on GitHub (pinned to 6c226ddcb3)
Solutions
- Find and stop the process holding the port: `ss -ltnp | grep <port>` or `lsof -i :<port>`, then kill it.
- Change the notification port in the exporter config to a free one.
- Verify the endpoint address format in the config (e.g. 0.0.0.0:PORT or 127.0.0.1:PORT).
- If two exporters are intended, give each its own notification port.
Defensive patterns
Strategy: validation
Validate before calling
// Before starting the exporter, verify the notification port is free:
let probe = tokio::net::TcpListener::bind(notification_endpoint).await;
match probe {
Ok(l) => drop(l), // port free; brief TOCTOU window is acceptable at startup
Err(e) if e.kind() == std::io::ErrorKind::AddrInUse => {
return Err(anyhow::anyhow!("notification port already in use: {notification_endpoint}"));
}
Err(e) => return Err(e.into()),
} Prevention
- Assign each exporter instance a distinct notification port in its config.
- In containers, let the orchestrator allocate host ports instead of hardcoding.
- After startup, health-check the gRPC endpoint (e.g. grpcurl health check) to confirm it bound.
When it happens
Trigger: Starting linera-exporter when the notification server port (from the config's notification section) is already bound by another process - a second exporter instance, a validator proxy using the same port, or a leftover process. Also a malformed endpoint address in the config.
Common situations: Duplicate exporter instances in a container/k8s setup pointing at the same port; port collision between the exporter's notification server and another service; stale process still holding the port after a crash.
Related errors
- Unable to read the configuration file
- Invalid configuration file format
- Failed to create log file
- Failed to clone storage
- Failed to bind to address
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/98a4b678bb544e34.
Report an issue: GitHub.