Skip to content

fix: null-safe ApiError + jittered retry backoff for getAllBatch ECONNRESET (v1.3.0) - #110

Merged
alphahlee merged 1 commit into
masterfrom
sdk/econnreset-batch-params-local
Jul 13, 2026
Merged

fix: null-safe ApiError + jittered retry backoff for getAllBatch ECONNRESET (v1.3.0)#110
alphahlee merged 1 commit into
masterfrom
sdk/econnreset-batch-params-local

Conversation

@alphahlee

@alphahlee alphahlee commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Problem

getAllBatch / getAllAssetsBatch crashed with Cannot read properties of undefined (reading 'status') when a page fetch hit a network error (e.g. ECONNRESET): the catch handler passed e.response (undefined for a network error) into throwResponseError, whose ApiError getters then dereferenced this.resp.status. Observed net-failing udst/udstsso assets on deployed connector nightlies.

Changes (single commit, rebased onto master)

  • famis_client.ts getAllBatch catch: rethrow the real error when there is no HTTP response, instead of fabricating an ApiError from undefined.
  • errors.ts null-safe isAuthorizationError / toString getters (this.resp?.…) so a response-less ApiError can't throw.
  • axios-retry: retries 2 → 6 (via a RETRIES const referenced by the onRetry log too) with jittered exponential backoff (base 1/2/4/8/16/30s + up to 25% jitter) so a transient reset has time to clear; jitter de-synchronizes concurrent getAllBatch pages (no thundering-herd). The ECONNABORTED (timeout) one-retry cap is unchanged.
  • onRetry logging ([facility360] retry N/6 …) so absorbed resets are visible even with debug off.
  • Add CHANGELOG.md (Keep a Changelog format) with the 1.3.0 entry + a 1.2.0 backfill.
  • Bump to 1.3.0not a new API (no signature change); the minor bump signals a notable behavior change (the retry policy affects every autoRetry consumer's failure latency) rather than a silent patch.

Explicitly NOT included

No { pageSize, maxConcurrent } opts and no per-fetch getAllBatch log line — those were local-testing scaffolding; the connector never uses the opts and signatures stay identical to master. (Corrects the earlier draft of this description, which mentioned them.)

Compatibility

No signature/API change. The only change reaching every autoRetry consumer is the retry policy — a failure-latency change on a genuinely down endpoint (~6ms → up to ~61s before giving up), not a correctness change. retryCondition still limits retries to network/idempotent errors + one retry on timeout (no retry-storm on 4xx). Note validateStatus: () => true is unchanged, so transient FAMIS 5xx are still surfaced via throwResponseError (not axios-retried) — the retry policy targets network errors/timeouts, which is the ECONNRESET case here. The request_timeout retry test was updated for the new count using fake timers (no real wall-clock cost).

Validation

Full local-connector fleet nightly (2026-07-10): 0 assets-loader failures across 1,920 loader runs; udst/udstsso (241k each) completed. 0 ECONNRESETs fired locally — the reset appears specific to the Cloud Run → FAMIS egress path, so the retry-absorbs-a-reset path should be confirmed post-deploy via the [facility360] retry log lines. All 94 SDK unit tests pass.

🤖 Generated with Claude Code

@alphahlee
alphahlee force-pushed the sdk/econnreset-batch-params-local branch from 0e24605 to 93fa81e Compare July 13, 2026 18:21
@alphahlee alphahlee self-assigned this Jul 13, 2026
@alphahlee
alphahlee force-pushed the sdk/econnreset-batch-params-local branch from 93fa81e to 6bfc124 Compare July 13, 2026 20:51
…ered retry backoff) — v1.3.0

getAllBatch / getAllAssetsBatch crashed with "Cannot read properties of undefined
(reading 'status')" when a page fetch hit a network error (e.g. ECONNRESET): the
catch handler passed `e.response` (undefined for a network error) into
throwResponseError, whose ApiError getters then dereferenced `this.resp.status`.

- famis_client.ts getAllBatch catch: rethrow the real error when there is no HTTP
  response, instead of fabricating an ApiError from `undefined`.
- errors.ts: null-safe `isAuthorizationError` and `message` getters; surface the
  underlying cause when there is no response.
- axios-retry: retries 2 -> 6 with jittered exponential backoff (base 1/2/4/8/16/30s
  + up to 25% jitter) so a transient reset has time to clear; jitter de-synchronizes
  the many concurrent pages of getAllBatch (no thundering-herd re-hammer). The
  ECONNABORTED (timeout) one-retry cap is unchanged.
- onRetry logging (`[facility360] retry N/6 ...`) so absorbed resets are visible in
  logs even with `debug` off.
- bump version to 1.3.0 (behavior change: broader retry policy).

Backward compatible: no signature/API change for existing callers; the only shared
change is the retry policy (more retries + real backoff), a failure-latency change,
not a correctness one. request_timeout retry test updated for the new count (fake
timers, no real wall-clock cost).

Note: the retry-*absorbs*-a-reset path was not exercised in the 2026-07-10 fleet run
(0 resets fired locally; the reset appears specific to the Cloud Run -> FAMIS egress
path). Confirm post-deploy via the `[facility360] retry` log lines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alphahlee
alphahlee force-pushed the sdk/econnreset-batch-params-local branch from 6bfc124 to aa1b87d Compare July 13, 2026 21:02
@alphahlee
alphahlee merged commit ae95aef into master Jul 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants