hcengineering/platform · warning

Failed to send statistics

Error message

Failed to send statistics

What it means

This is a warning logged by the server's statistics/metrics reporting timer when an attempt to POST stats to the configured statsUrl fails. It is logged every second failure (errorToSend % 2 === 0) and includes the underlying error code (e.g. from undici), message, and cause. It indicates the metrics/telemetry endpoint is unreachable or rejecting requests; it does not affect core server functionality.

Source

Thrown at foundations/server/packages/core/src/stats.ts:108

  } else {
    metricsContext = new MeasureMetricsContext(serviceName, {}, {}, newMetrics())
  }

  const statsUrl = ops?.statsUrl ?? process.env.STATS_URL

  let errorToSend = 0

  if (statsUrl !== undefined) {
    metricsContext.info('using stats url', { statsUrl, service: serviceName ?? '' })
    const serviceId = encodeURIComponent(os.hostname() + '-' + serviceName)

    let prev: Promise<void> | Promise<any> | undefined
    const handleError = (err: any): void => {
      errorToSend++
      if (errorToSend % 2 === 0) {
        const code = err?.code ?? err?.cause?.code
        if (code !== 'UND_ERR_SOCKET') {
          metricsContext.warn('Failed to send statistics', {
            service: serviceName,
            statsUrl,
            code,
            message: err?.message,
            causeMessage: err?.cause?.message,
            err
          })
        }
      }
      prev = undefined
    }

    const intTimer = setInterval(() => {
      try {
        if (prev !== undefined) {
          // In case of high load, skip
          return
        }

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Verify the statsUrl configuration points to a reachable, healthy stats collector service
  2. Check network/firewall/DNS from the server host to the stats endpoint (curl the statsUrl)
  3. Inspect the logged code/causeMessage to identify the transport error (UND_ERR_SOCKET, ECONNREFUSED, etc.) and fix accordingly
  4. If the collector is intentionally absent, disable stats reporting in config instead of letting it fail repeatedly
  5. Upgrade undici/Node if errors are UND_ERR_SOCKET caused by known connection-reuse bugs

Example fix

// before (config)
STATS_URL=http://stats-internal:4900/ingest
// after (correct reachable collector or disabled)
STATS_URL=http://stats-collector.monitoring.svc:4900/ingest
# or
STATS_ENABLED=false
Defensive patterns

Strategy: retry

Validate before calling

const ok = await fetch(statsUrl, { method: 'HEAD' }).then(r => r.ok).catch(() => false)
if (!ok) console.warn('stats endpoint unreachable, skipping report')

Type guard

function hasErrorCode(err: unknown): err is { code: string } {
  return typeof err === 'object' && err !== null && 'code' in err && typeof (err as any).code === 'string'
}

Try / catch

try {
  await sendStats()
} catch (err) {
  const code = (err as any)?.code ?? (err as any)?.cause?.code
  if (code !== 'UND_ERR_SOCKET') logger.warn('stats send failed', { code })
  // swallow: stats loss is non-fatal
}

Prevention

When it happens

Trigger: The intTimer periodic callback invokes the stats send (an HTTP fetch/undici request to statsUrl) and the promise rejects — e.g. DNS failure, connection refused/reset, UND_ERR_SOCKET, timeout, or non-2xx response from the stats collector.

Common situations: Stats collector service is down or misconfigured; statsUrl points to a wrong host/port in server config; network egress restrictions or firewall blocks the collector; transient socket errors under load (UND_ERR_SOCKET from undici).

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/4cb500670a8d6eaf. Report an issue: GitHub.