A network failure is logged and recorded as fetch failed, with the reason dropped #196

Open
opened 2026-10-14 09:29:21 +02:00 by clawbot · 0 comments
Collaborator

Severity: medium

Where: run in src/cli-run.ts prints err.message; errorMessage, recordFailure and classify in src/backup.ts read only the message too.

Problem: Node's fetch rejects every failure below HTTP (no route, name not found, connection refused, TLS, a reset) with a TypeError whose message is just fetch failed; the reason (ECONNREFUSED, ENOTFOUND, ...) is on its cause. The retry policy looks there (README, "Retries and timeouts"), but what is printed and recorded does not. After the retries, a run from cron leaves:

  • when the refresh fails: the one line quak: fetch failed, exit 1, and nothing says whether the name, the route or the server was the problem;
  • when a download fails: FAILED original a.jpg: fetch failed, and in failures.json "error": "fetch failed" with "classification": "unknown", because the classifier searches the message for the errno, where it is not.

An operator reading the log cannot tell a dead network from a bug, and the ledger records the wrong kind of failure for the most common transient one.

Reproduced: a backup through the fake Ente server of test/ente-server.ts with a fetch that rejects with new TypeError("fetch failed", { cause: Object.assign(new Error("connect ECONNREFUSED 1.2.3.4:443"), { code: "ECONNREFUSED" }) }), once for every request and once for the download only. Output as above; ECONNREFUSED appears nowhere in the log or the ledger.

Done when: the printed line and the ledger entry carry the messages of the error's cause chain, for example fetch failed: connect ECONNREFUSED 1.2.3.4:443, and classify sees the errno, so this failure is transient. Tests in test/cli/run.test.ts and test/cli/backup-end-to-end.test.ts pin the printed line and the ledger entry for a TypeError with an errno in cause.

Model: fable-5-1

Severity: medium **Where:** `run` in `src/cli-run.ts` prints `err.message`; `errorMessage`, `recordFailure` and `classify` in `src/backup.ts` read only the message too. **Problem:** Node's `fetch` rejects every failure below HTTP (no route, name not found, connection refused, TLS, a reset) with a `TypeError` whose message is just `fetch failed`; the reason (`ECONNREFUSED`, `ENOTFOUND`, ...) is on its `cause`. The retry policy looks there (README, "Retries and timeouts"), but what is printed and recorded does not. After the retries, a run from cron leaves: - when the refresh fails: the one line `quak: fetch failed`, exit 1, and nothing says whether the name, the route or the server was the problem; - when a download fails: `FAILED original a.jpg: fetch failed`, and in `failures.json` `"error": "fetch failed"` with `"classification": "unknown"`, because the classifier searches the message for the errno, where it is not. An operator reading the log cannot tell a dead network from a bug, and the ledger records the wrong kind of failure for the most common transient one. **Reproduced:** a backup through the fake Ente server of `test/ente-server.ts` with a `fetch` that rejects with `new TypeError("fetch failed", { cause: Object.assign(new Error("connect ECONNREFUSED 1.2.3.4:443"), { code: "ECONNREFUSED" }) })`, once for every request and once for the download only. Output as above; `ECONNREFUSED` appears nowhere in the log or the ledger. **Done when:** the printed line and the ledger entry carry the messages of the error's `cause` chain, for example `fetch failed: connect ECONNREFUSED 1.2.3.4:443`, and `classify` sees the errno, so this failure is `transient`. Tests in `test/cli/run.test.ts` and `test/cli/backup-end-to-end.test.ts` pin the printed line and the ledger entry for a `TypeError` with an errno in `cause`. Model: fable-5-1
clawbot added this to the 1.0.0 milestone 2026-10-14 09:29:21 +02:00
Sign in to join this conversation.