api: separate timeout abort retries from intentional aborts

This commit is contained in:
Scott Idem
2026-05-21 15:46:30 -04:00
parent d5d552a029
commit f5cf1ef398
3 changed files with 68 additions and 6 deletions

View File

@@ -203,7 +203,11 @@ export const post_object = async function post_object({
// Declared at loop scope (not inside try) so the catch block can clearTimeout.
// Fresh controller per attempt — same rationale as api_get_object.ts.
const controller = new AbortController();
// AbortError is not specific enough by itself. Distinguish timeout-aborts
// (retryable transient class) from intentional caller aborts (fail-fast).
let did_timeout_abort = false;
const timeoutId = setTimeout(() => {
did_timeout_abort = true;
console.warn(`API POST: Request timed out after ${timeout}ms (attempt ${attempt}/${retry_count}).`);
controller.abort();
}, timeout);
@@ -254,9 +258,16 @@ export const post_object = async function post_object({
(response.name === 'TypeError' ||
response.name === 'AbortError'))
) {
// AbortError = intentional cancel (timeout fired, or caller aborted).
// Never retry — the abort was deliberate.
if (response.name === 'AbortError') return false;
// Retry timeout-aborts from this helper; do not retry caller aborts
// (route change/unmount/manual cancellation).
if (response.name === 'AbortError') {
if (did_timeout_abort) {
throw new Error(
`Timeout abort (attempt ${attempt}/${retry_count}) after ${timeout}ms`
);
}
return false;
}
// TypeError = transient network failure. Throw into the retry loop
// so backoff-and-retry applies. Same fix as api_get_object.ts — see