Skip to content

Production reliability

Predictable failure modes when @media-sdk/core runs against real provider APIs. This matrix links scenarios to expected errors and test coverage.

For handler patterns, see Errors and Error handling recipes.

Failure mode matrix

ScenarioExpected behaviorMediaError / error typeTested
HTTP 401 UnauthorizedReject; no cache writeMediaError, status: 401Yes — FetchHttpClient.test.ts
HTTP 403 ForbiddenReject; no cache writeMediaError, status: 403Yes — FetchHttpClient.test.ts, PixabayMediaClient.test.ts
HTTP 404 Not FoundRejectMediaError, status: 404Yes — FetchHttpClient.test.ts
HTTP 429 Too Many RequestsReject; no automatic retryMediaError, status: 429Yes — FetchHttpClient.test.ts
HTTP 5xx (500, 503)RejectMediaError, status setYes — FetchHttpClient.test.ts
Malformed JSON on 2xxReject from response.json()Native Error (not MediaError)Yes — FetchHttpClient.test.ts
Missing API key (ApiKeyProvider(""))Throw at constructionNative ErrorYes — ApiKeyProvider.test.ts
Network failure (fetch throws)Propagate fetch errorNative Error (not MediaError)Yes — FetchHttpClient.test.ts
Request abort (AbortSignal)AbortError / DOMExceptionNot MediaErrorYes — FetchHttpClient.test.ts, RequestManager.test.ts
Provider HTTP error via clientPropagate MediaError from HttpClientMediaError + statusYes — PexelsMediaClient.test.ts, PixabayMediaClient.test.ts
Provider network error via clientPropagate native ErrorNot MediaErrorYes — PexelsMediaClient.test.ts
Failed request not cachedRetry allowed on same keyYes — RequestManager.test.ts, PexelsMediaClient.test.ts
No built-in fetch timeoutfetch runs until browser/runtime completes or abortsDocumented (no SDK timeout)
Rate limit backoffNot implemented — app responsibilityDocumented
Unsupported operationMediaError, code: UNSUPPORTED_CAPABILITYYes — provider tests
Unsupported filter keyMediaError, code: UNSUPPORTED_FILTERYes — filter validation tests
Invalid filter valueMediaError, code: INVALID_FILTER_VALUEYes — filter validation tests

No built-in timeout

FetchHttpClient does not set AbortSignal.timeout or a custom deadline. Hung requests depend on the runtime and network stack.

Workaround: inject a custom HttpClient that wraps fetch with a timeout:

ts
import {
  FetchHttpClient,
  type HttpClient,
  type HttpRequestOptions,
} from "@media-sdk/core";

function withTimeout(
  inner: HttpClient,
  timeoutMs: number,
): HttpClient {
  return {
    get<T>(url: string, options?: HttpRequestOptions): Promise<T> {
      const controller = new AbortController();
      const timeoutId = setTimeout(
        () => controller.abort(),
        timeoutMs,
      );

      const signal = options?.signal;
      if (signal) {
        if (signal.aborted) {
          controller.abort();
        } else {
          signal.addEventListener("abort", () => controller.abort(), {
            once: true,
          });
        }
      }

      return inner
        .get(url, { ...options, signal: controller.signal })
        .finally(() => clearTimeout(timeoutId));
    },
  };
}

const client = new PexelsMediaClient(auth, {
  httpClient: withTimeout(
    new FetchHttpClient(() => auth.getKey()),
    10_000,
  ),
});

Rate limits (429)

Providers enforce per-key limits. The SDK surfaces status === 429 as MediaError. Backoff and retry are app responsibilities — the SDK does not retry automatically.

Cache on failure

RequestManager does not cache rejected requests. A failed search can be retried without stale error entries. See Provider pages for TTL and deduplication.

Starter messaging

ConditionSuggested UX
Missing env API keyFail at createMediaClient / ApiKeyProvider with clear env var name
401 / 403"Check API key for active provider"
429"Rate limited — wait or reduce request rate"
Network / timeoutGeneric retry message; check connectivity