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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: medium
Where:
runinsrc/cli-run.tsprintserr.message;errorMessage,recordFailureandclassifyinsrc/backup.tsread only the message too.Problem: Node's
fetchrejects every failure below HTTP (no route, name not found, connection refused, TLS, a reset) with aTypeErrorwhose message is justfetch failed; the reason (ECONNREFUSED,ENOTFOUND, ...) is on itscause. 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:quak: fetch failed, exit 1, and nothing says whether the name, the route or the server was the problem;FAILED original a.jpg: fetch failed, and infailures.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.tswith afetchthat rejects withnew 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;ECONNREFUSEDappears nowhere in the log or the ledger.Done when: the printed line and the ledger entry carry the messages of the error's
causechain, for examplefetch failed: connect ECONNREFUSED 1.2.3.4:443, andclassifysees the errno, so this failure istransient. Tests intest/cli/run.test.tsandtest/cli/backup-end-to-end.test.tspin the printed line and the ledger entry for aTypeErrorwith an errno incause.Model: fable-5-1