Skip to content

fix: normalize error types at api boundary - #211

Draft
duyhungtnn wants to merge 28 commits into
masterfrom
fix/normalize-error-types-at-api-boundary
Draft

fix: normalize error types at api boundary#211
duyhungtnn wants to merge 28 commits into
masterfrom
fix/normalize-error-types-at-api-boundary

Conversation

@duyhungtnn

@duyhungtnn duyhungtnn commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

Problem

Abort and timeout errors were misclassified as UnknownErrorMetricsEvent. When AbortSignal.timeout() or AbortController.abort() fires, Node's https.request emits AbortError { code: 'ABORT_ERR' } — a shape that neither toErrorMetricsEvent nor the cache processors handled.

The deeper issue: error conversion was scattered across three places with inconsistent logic.

What changed

Single conversion pointpostRequestWithRetry now normalizes all errors to BKTBaseError via toBKTError before throwing. Every public API client method is guaranteed to only throw BKTBaseError subclasses.

toBKTError — extended to handle ABORT_ERR (Node abort code) and name-based DOMException (AbortError, TimeoutError), both mapping to TimeoutError.

toErrorMetricsEvent — signature narrowed from (e: any) to (e: BKTBaseError). Body replaced with pure instanceof dispatch, removing the isNodeError block and fragile e.name string checks.

Cache processors — removed hand-rolled isDeadlineExceeded + new TimeoutError() wrapping; now rely on the API client contract and check instanceof TimeoutError directly.

isDeadlineExceeded — deleted from pollController.ts (no remaining callers).

Tests

  • 3 new toBKTError cases covering ABORT_ERR and DOMException shapes
  • 2 new api_retry.ts boundary tests: hanging server + AbortSignal.timeout / AbortController.abort both produce TimeoutError
  • error_to_metrics_event.ts rewritten to use BKTBaseError subclasses as inputs
  • All API integration tests updated to assert the correct BKTBaseError subclass
  • Cache processor mocks updated to reject with TimeoutError to honour the new contract

duyhungtnn added 26 commits May 31, 2026 22:03
Update DEFAULT_FLUSH_INTERVAL_MILLIS from 10_000 to 30_000 and revise comments to clarify that the minimum flush interval remains 10s while the default is 30s. This ensures the default flush cadence is less frequent than the enforced minimum and makes intent explicit in the code comments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR centralizes error normalization at the API boundary so abort/timeout shapes (including Node ABORT_ERR and DOMException AbortError/TimeoutError) are consistently surfaced as BKTBaseError subclasses and correctly classified in metrics and cache processors.

Changes:

  • Normalize all API client failures via toBKTError in postRequestWithRetry, so public API methods only throw BKTBaseError subclasses.
  • Refactor toErrorMetricsEvent to accept BKTBaseError and dispatch purely via instanceof, removing string/code heuristics.
  • Simplify cache processors by removing deadline-detection helpers and relying on the normalized error contract; update/add tests for abort/timeout boundaries.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/utils/pollController.ts Removes the now-unused isDeadlineExceeded helper after shifting abort/timeout normalization elsewhere.
src/objects/metricsEvent.ts Narrows toErrorMetricsEvent input to BKTBaseError and switches to instanceof-based classification.
src/objects/errors.ts Extends toBKTError to map ABORT_ERR and DOMException abort/timeout names to TimeoutError.
src/client.ts Ensures metrics event conversion uses normalized BKTBaseError at the boundary.
src/cache/processor/segmentUsersCacheProcessor.ts Routes errors through normalization and uses TimeoutError directly (drops deadline helper).
src/cache/processor/featureFlagCacheProcessor.ts Same cache-processor error handling refactor as segment users.
src/api/client.ts Converts the retry wrapper to a single normalization point by catching and throwing toBKTError(...).
src/tests/to_bkt_error.ts Adds unit coverage for ABORT_ERR and DOMException abort/timeout shapes.
src/tests/error_to_metrics_event.ts Updates tests to use BKTBaseError subclasses rather than raw node/DOM errors.
src/tests/api_retry.ts Adds API-boundary tests asserting aborts/timeouts normalize to TimeoutError.
src/tests/api_retry_after.ts Updates retry-after tests to reflect API-boundary normalization for 503s.
src/tests/api_failed.ts Updates integration tests to assert normalized 500s as InternalServerError.
src/tests/promise_retriable.ts Comment-only arrow typography update.
src/tests/cache/processor/segementUsersCache/cancellation.ts Updates cache cancellation mocks to reject with TimeoutError under the new contract.
src/tests/cache/processor/featureCache/cancellation.ts Same cancellation-mock update for feature cache processor.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/api/client.ts
Comment on lines +123 to +129
try {
return await promiseRetriable(
(s) => this.postRequest<T>(url, chunk, s),
this.retryPolicy,
isRetryable,
signal);
} catch (e) {
Comment on lines 6 to 9
import { APIClient } from '../api/client';
import { User } from '../bootstrap';
import { InvalidStatusError } from '../objects/errors';
import { InvalidStatusError, TimeoutError, UnauthorizedError } from '../objects/errors';
import { RetryPolicy } from '../utils/promiseRetriable';
Comment on lines +109 to +110
this.pushErrorMetricsEvent(bktError);
}
Comment on lines +104 to +105
this.pushErrorMetricsEvent(bktError);
}
Comment on lines +263 to +265
if (e instanceof ForbiddenError) {
logger?.error('An forbidden error occurred. Please check your API Key.');
return null;
@duyhungtnn
duyhungtnn changed the base branch from chore/retry-on-499 to master June 6, 2026 15:39
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