stamparm/maltrail · error

unable to send to remote logging endpoint

Error message

unable to send to remote logging endpoint '{endpoint}'

What it means

The UDP send to the remote logging endpoint failed even after the sensor dropped and recreated the socket and retried once. The first failure is transparently retried with a fresh socket; only a second failure is logged, incremented in remote_log_errors, and the event is not delivered remotely.

Solutions

  1. Verify network reachability to '{endpoint}' from the sensor host (ping, firewall rules, security groups allowing outbound UDP).
  2. Confirm the remote collector is running and reachable; UDP send errors often indicate ICMP port-unreachable from a dead collector.
  3. Check routing/MTU if errors began after a network change; try reducing datagram size.
  4. Note events are still counted in events_written but not delivered remotely — pair remote logging with the local event log so records survive network outages.
Defensive patterns

Strategy: try-catch

Validate before calling

# check outbound UDP reachability to the collector before relying on remote logging
nc -vzu <collector-host> <udp-port>

Prevention

When it happens

Trigger: send_datagram's s.send_to(data, addr) fails, a fresh socket is bound and the retry send also fails (or !retried), while writing an event via write_line — typically network unreachable, ICMP port unreachable, or interface down.

Common situations: Firewall dropping outbound UDP to the collector; collector host down (ICMP unreachable surfaces as send errors on connected paths); routing changes; MTU issues for large datagrams; sensor deployed in a network-isolated segment.

Related errors


AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13). Data as JSON: /api/errors/6aaee1e706b6e1c9. Report an issue: GitHub.

Appendix: source

Thrown at sensor/src/output.rs:517

            self.remote_log_errors += 1;
            return;
        };
        if s.send_to(data, addr).is_err() {
            // Drop and recreate the socket once, exactly like `_send_datagram`.
            let fresh = UdpSocket::bind(bind).ok();
            let retried = match &fresh {
                Some(f) => f.send_to(data, addr).is_ok(),
                None => false,
            };
            if is_v6 {
                self.sock6 = fresh;
            } else {
                self.sock4 = fresh;
            }
            if !retried {
                // The second failure used to be discarded outright, so a remote-only deployment
                // could lose every event while `events_written` kept climbing.
                log_error(&format!("unable to send to remote logging endpoint '{endpoint}'"), true);
                self.remote_log_errors += 1;
            }
        }
    }

    /// `core/log.py:flush_condensed_events()`
    pub fn flush_condensed(&mut self) {
        if self.condensed.is_empty() {
            self.last_condense_flush = Instant::now();
            return;
        }
        let snapshot: Vec<Vec<Event>> = self.condensed.drain().map(|(_, v)| v).collect();
        self.last_condense_flush = Instant::now();

        for events in snapshot {
            if let Some(merged) = merge_events(&events) {
                self.log_event(&merged, false, true);
            }

View on GitHub (pinned to 77cfb06d76)