{"record":{"id":"4cb500670a8d6eaf","repo":"hcengineering/platform","slug":"failed-to-send-statistics","errorCode":null,"errorMessage":"Failed to send statistics","messagePattern":"Failed to send statistics","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"foundations/server/packages/core/src/stats.ts","lineNumber":108,"sourceCode":"  } else {\n    metricsContext = new MeasureMetricsContext(serviceName, {}, {}, newMetrics())\n  }\n\n  const statsUrl = ops?.statsUrl ?? process.env.STATS_URL\n\n  let errorToSend = 0\n\n  if (statsUrl !== undefined) {\n    metricsContext.info('using stats url', { statsUrl, service: serviceName ?? '' })\n    const serviceId = encodeURIComponent(os.hostname() + '-' + serviceName)\n\n    let prev: Promise<void> | Promise<any> | undefined\n    const handleError = (err: any): void => {\n      errorToSend++\n      if (errorToSend % 2 === 0) {\n        const code = err?.code ?? err?.cause?.code\n        if (code !== 'UND_ERR_SOCKET') {\n          metricsContext.warn('Failed to send statistics', {\n            service: serviceName,\n            statsUrl,\n            code,\n            message: err?.message,\n            causeMessage: err?.cause?.message,\n            err\n          })\n        }\n      }\n      prev = undefined\n    }\n\n    const intTimer = setInterval(() => {\n      try {\n        if (prev !== undefined) {\n          // In case of high load, skip\n          return\n        }","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/server/packages/core/src/stats.ts#L90-L126","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Verify the statsUrl configuration points to a reachable, healthy stats collector service","Check network/firewall/DNS from the server host to the stats endpoint (curl the statsUrl)","Inspect the logged code/causeMessage to identify the transport error (UND_ERR_SOCKET, ECONNREFUSED, etc.) and fix accordingly","If the collector is intentionally absent, disable stats reporting in config instead of letting it fail repeatedly","Upgrade undici/Node if errors are UND_ERR_SOCKET caused by known connection-reuse bugs"],"exampleFix":"// before (config)\nSTATS_URL=http://stats-internal:4900/ingest\n// after (correct reachable collector or disabled)\nSTATS_URL=http://stats-collector.monitoring.svc:4900/ingest\n# or\nSTATS_ENABLED=false","handlingStrategy":"retry","validationCode":"const ok = await fetch(statsUrl, { method: 'HEAD' }).then(r => r.ok).catch(() => false)\nif (!ok) console.warn('stats endpoint unreachable, skipping report')","typeGuard":"function hasErrorCode(err: unknown): err is { code: string } {\n  return typeof err === 'object' && err !== null && 'code' in err && typeof (err as any).code === 'string'\n}","tryCatchPattern":"try {\n  await sendStats()\n} catch (err) {\n  const code = (err as any)?.code ?? (err as any)?.cause?.code\n  if (code !== 'UND_ERR_SOCKET') logger.warn('stats send failed', { code })\n  // swallow: stats loss is non-fatal\n}","preventionTips":["Monitor the stats endpoint with an uptime probe","Make stats reporting optional/disablable via config","Log code + causeMessage (as the source does) to speed diagnosis","Alert on sustained send failures rather than single blips"],"tags":["metrics","network","monitoring"],"backgroundTag":"stats-endpoint-unreachable","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}