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>(&notification.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

  1. Find and stop the process holding the port: `ss -ltnp | grep <port>` or `lsof -i :<port>`, then kill it.
  2. Change the notification port in the exporter config to a free one.
  3. Verify the endpoint address format in the config (e.g. 0.0.0.0:PORT or 127.0.0.1:PORT).
  4. 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

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


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/98a4b678bb544e34. Report an issue: GitHub.