{"record":{"id":"b07e2e1a3902b18d","repo":"MagicMirrorOrg/MagicMirror","slug":"this-logcontext-this-shortenurl-messag","errorCode":null,"errorMessage":"${this.logContext}${this.#shortenUrl()} - ${message} Retry #${this.networkErrorCount} in ${Math.round(nextDelay / 1000)}s.","messagePattern":"(.+?)(.+?) - (.+?) Retry #(.+?) in (.+?)s\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"js/http_fetcher.js","lineNumber":330,"sourceCode":"\t\t\t\tthis.emit(\"error\", errorInfo);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst isTimeout = error.name === \"AbortError\";\n\t\t\tconst message = isTimeout ? `Request timeout after ${this.timeout}ms` : `Network error: ${error.message}`;\n\n\t\t\tthis.networkErrorCount = Math.min(this.networkErrorCount + 1, this.maxRetries);\n\t\t\tconst exhausted = this.networkErrorCount >= this.maxRetries;\n\n\t\t\tif (exhausted) {\n\t\t\t\tnextDelay = this.reloadInterval;\n\t\t\t\tLog.error(`${this.logContext}${this.#shortenUrl()} - ${message} Max retries reached, retrying at configured interval (${Math.round(nextDelay / 1000)}s).`);\n\t\t\t} else {\n\t\t\t\tnextDelay = HTTPFetcher.calculateBackoffDelay(this.networkErrorCount, {\n\t\t\t\t\tmaxDelay: this.reloadInterval\n\t\t\t\t});\n\t\t\t\tconst retryMsg = `${this.logContext}${this.#shortenUrl()} - ${message} Retry #${this.networkErrorCount} in ${Math.round(nextDelay / 1000)}s.`;\n\t\t\t\tif (this.networkErrorCount <= 2) {\n\t\t\t\t\tLog.warn(retryMsg);\n\t\t\t\t} else {\n\t\t\t\t\tLog.error(retryMsg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst errorInfo = this.#createErrorInfo(\n\t\t\t\tmessage,\n\t\t\t\tnull,\n\t\t\t\t\"NETWORK_ERROR\",\n\t\t\t\tnextDelay,\n\t\t\t\terror\n\t\t\t);\n\t\t\tthis.emit(\"error\", errorInfo);\n\t\t} finally {\n\t\t\tclearTimeout(timeoutId);\n\t\t}\n\n\t\tthis.scheduleNextFetch(nextDelay);","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/MagicMirrorOrg/MagicMirror/blob/4b4a59534f7da01e4030e46029fe9dd649a7675e/js/http_fetcher.js#L312-L348","documentation":"In HTTPFetcher.fetch, when a network-level error occurs (fetch rejection such as DNS failure, connection refused/reset, timeout), the fetcher computes an exponential backoff via calculateBackoffDelay capped at reloadInterval, logs a retry message as warning for the first two attempts and error afterwards, and increments networkErrorCount for each subsequent retry.","triggerScenarios":"fetch() throws while requesting a remote resource — unreachable host, offline network, TLS errors, connection timeout (connect timeout set by the undici Agent's fetch_timeout) — triggering the retry/backoff path.","commonSituations":"WiFi drop or router reboot on a Raspberry Pi; firewalled/air-gapped networks; the remote API endpoint being down or DNS misconfigured; invalid proxy settings blocking outbound HTTPS.","solutions":["Verify network connectivity from the mirror host (ping/curl the endpoint URL).","Check the shortened URL in the log for a typo or expired domain; correct the address in the module config.","Increase fetch_timeout or reloadInterval if the network is slow/flaky to give retries more room.","Fix DNS/proxy/firewall settings so outbound HTTPS to the endpoint works.","Wait — the fetcher retries automatically with exponential backoff capped at the reload interval."],"exampleFix":"// before\ncurl https://api.example.com/data  # connection refused\n// after\ncurl https://api.correct-domain.com/data  # fix URL in module config / restore network","handlingStrategy":"retry","validationCode":"// Before starting periodic fetches, verify reachability\nasync function reachable(url, timeoutMs = 5000) {\n  try {\n    const ctl = new AbortController();\n    const t = setTimeout(() => ctl.abort(), timeoutMs);\n    await fetch(url, { method: \"HEAD\", signal: ctl.signal });\n    clearTimeout(t);\n    return true;\n  } catch { return false;\n  }\n}","typeGuard":"function isNetworkError(err) {\n  return err instanceof TypeError || /ECONNREFUSED|ENOTFOUND|ETIMEDOUT|ECONNRESET/.test(err?.cause?.code ?? \"\");\n}","tryCatchPattern":"try {\n  const res = await fetchWithTimeout(url, 10000);\n} catch (err) {\n  networkErrorCount++;\n  const nextDelay = Math.min(reloadInterval, 1000 * 2 ** networkErrorCount);\n  Log.warn(`${url} failed (${err.cause?.code ?? err.message}); retry #${networkErrorCount} in ${nextDelay / 1000}s`);\n  setTimeout(fetch, nextDelay);\n}","preventionTips":["Monitor the Pi's WiFi/connection health (e.g. a watchdog or ping check).","Pin and verify endpoint URLs; DNS typos show up as ENOTFOUND in the log.","Set a sane fetch_timeout so hangs become recoverable errors instead of silent stalls.","Cap backoff at reloadInterval (as the library does) so retries stay periodic."],"tags":["network","retry","backoff","fetch"],"backgroundTag":"network-error-retry-backoff","analyzedSha":"4b4a59534f7da01e4030e46029fe9dd649a7675e","analyzedAt":"2026-08-31T21:49:42.591Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}