The binary make install builds does not retry a connection that fails, or record it as transient #206

Closed
opened 2026-10-16 02:23:29 +02:00 by clawbot · 2 comments
Collaborator

Severity: medium

Where: isRetryable in src/retry.ts and classify in src/backup.ts, as they run in ~/bin/quak, the single binary make install builds with bun (build-bin in the Makefile), which is what the README's "Running the backup from cron" runs.

Problem: under bun, fetch does not reject a failure below HTTP with Node's TypeError("fetch failed") and an errno on cause. A refused connection comes back as an error whose message is Unable to connect. Is the computer able to access the url?, with no cause, and with bun's own code rather than an errno. isRetryable recognises neither the TypeError nor any code in TRANSPORT_CODES, so the installed binary does not retry a refused, unresolvable or reset connection at all, and the backup's ledger does not record it as transient. The README's "Retries and timeouts" describes the Node behaviour only. Found while reviewing #203: the binary built by make build-bin, run with quak login and quak backup against an address that refuses connections, printed quak: Unable to connect. Is the computer able to access the url?.

Done when: the errors bun's fetch gives for a refused connection, a name that does not resolve, a reset connection and a timeout (found by running the built binary, not assumed) are retried by isRetryable as their Node equivalents are, replayed by isSafeToReplay only when they mean no connection was made, and classified transient by the backup; tests in test/retry.test.ts (or wherever isRetryable is tested) pin each shape; README "Retries and timeouts" says the installed binary behaves the same. Starts after #203 is on next, since both change how these errors are read.

Model: opus-5-5

Severity: medium **Where:** `isRetryable` in `src/retry.ts` and `classify` in `src/backup.ts`, as they run in `~/bin/quak`, the single binary `make install` builds with bun (`build-bin` in the `Makefile`), which is what the README's "Running the backup from cron" runs. **Problem:** under bun, `fetch` does not reject a failure below HTTP with Node's `TypeError("fetch failed")` and an errno on `cause`. A refused connection comes back as an error whose message is `Unable to connect. Is the computer able to access the url?`, with no `cause`, and with bun's own `code` rather than an errno. `isRetryable` recognises neither the `TypeError` nor any code in `TRANSPORT_CODES`, so the installed binary does not retry a refused, unresolvable or reset connection at all, and the backup's ledger does not record it as `transient`. The README's "Retries and timeouts" describes the Node behaviour only. Found while reviewing https://git.eeqj.de/sneak/quak/pulls/203: the binary built by `make build-bin`, run with `quak login` and `quak backup` against an address that refuses connections, printed `quak: Unable to connect. Is the computer able to access the url?`. **Done when:** the errors bun's `fetch` gives for a refused connection, a name that does not resolve, a reset connection and a timeout (found by running the built binary, not assumed) are retried by `isRetryable` as their Node equivalents are, replayed by `isSafeToReplay` only when they mean no connection was made, and classified `transient` by the backup; tests in `test/retry.test.ts` (or wherever `isRetryable` is tested) pin each shape; README "Retries and timeouts" says the installed binary behaves the same. Starts after https://git.eeqj.de/sneak/quak/pulls/203 is on `next`, since both change how these errors are read. Model: opus-5-5
clawbot added this to the 1.0.0 milestone 2026-10-16 02:23:29 +02:00
Author
Collaborator

Plan.

  • First find the real shapes: build the binary with make build-bin and run it against an address that refuses connections, a name that does not resolve, a server that resets the connection, and one that never answers, and note each error's class, message, code and cause.
  • src/retry.ts is the retry classifier, so the change goes there. Each code bun gives for a transport failure joins TRANSPORT_CODES, and only those that prove no connection was made join CONNECT_CODES. A comment says which are bun's. classify in src/backup.ts already asks hasTransportCode, so it then calls them transient without a change of its own. If a bun error carries no code, isRetryable recognises it by class, as it recognises Node's TypeError, and the comment says why.
  • Tests in test/retry/retry.test.ts build each shape as found and pin isRetryable, isSafeToReplay and, through the backup, the transient class.
  • README "Retries and timeouts" says the installed binary is retried the same way. The paragraph under "Exit codes" that #203 added is corrected if it now says otherwise.

Model: opus-5-5

Plan. - First find the real shapes: build the binary with `make build-bin` and run it against an address that refuses connections, a name that does not resolve, a server that resets the connection, and one that never answers, and note each error's class, `message`, `code` and `cause`. - `src/retry.ts` is the retry classifier, so the change goes there. Each code bun gives for a transport failure joins `TRANSPORT_CODES`, and only those that prove no connection was made join `CONNECT_CODES`. A comment says which are bun's. `classify` in `src/backup.ts` already asks `hasTransportCode`, so it then calls them transient without a change of its own. If a bun error carries no code, `isRetryable` recognises it by class, as it recognises Node's `TypeError`, and the comment says why. - Tests in `test/retry/retry.test.ts` build each shape as found and pin `isRetryable`, `isSafeToReplay` and, through the backup, the `transient` class. - README "Retries and timeouts" says the installed binary is retried the same way. The paragraph under "Exit codes" that https://git.eeqj.de/sneak/quak/pulls/203 added is corrected if it now says otherwise. Model: opus-5-5
Author
Collaborator

Done in #207. The built binary showed that bun already gives ECONNRESET for a reset and a TimeoutError for a deadline, both already retried. What was missing were ConnectionRefused (a refused connection, a failed TLS handshake, and sometimes a name that does not resolve) and FailedToOpenSocket (a name that does not resolve). The PR adds both to src/retry.ts.

Model: opus-5-5

Done in https://git.eeqj.de/sneak/quak/pulls/207. The built binary showed that bun already gives `ECONNRESET` for a reset and a `TimeoutError` for a deadline, both already retried. What was missing were `ConnectionRefused` (a refused connection, a failed TLS handshake, and sometimes a name that does not resolve) and `FailedToOpenSocket` (a name that does not resolve). The PR adds both to `src/retry.ts`. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/quak#206