Errors
@media-sdk/core uses typed errors for HTTP failures, unsupported capabilities, and invalid filters. Branch on error.code and isAbortError — never parse error.message for control flow.
For copy-paste handlers and a decision tree, see Error handling recipes. For the full failure-mode matrix (HTTP statuses, timeout, network), see Production reliability.
Installation
pnpm add @media-sdk/core@^0.3.0Error taxonomy
| Failure | Error type | code | status |
|---|---|---|---|
| HTTP 4xx / 5xx | MediaError | optional | set |
| Unsupported operation (e.g. curated on Pixabay) | MediaError | UNSUPPORTED_CAPABILITY | — |
| Unsupported search filter key | MediaError | UNSUPPORTED_FILTER | — |
| Invalid filter value for provider | MediaError | INVALID_FILTER_VALUE | — |
Request cancelled via AbortSignal | AbortError / DOMException | — | — |
Network / fetch rejection | native Error | — | — |
| Invalid JSON on 2xx response | native Error | — | — |
Missing API key in ApiKeyProvider | native Error | — | — |
MediaError
class MediaError extends Error {
readonly status?: number;
readonly code?: string;
}Import from the package entry:
import { MediaError } from "@media-sdk/core";UNSUPPORTED_CAPABILITY
Thrown when the client does not support an operation:
import { ApiKeyProvider, PixabayMediaClient, MediaError } from "@media-sdk/core";
const pixabay = new PixabayMediaClient(
new ApiKeyProvider(process.env.PIXABAY_API_KEY!),
);
try {
await pixabay.getCuratedPhotos({ page: 1, perPage: 20 });
} catch (error) {
if (error instanceof MediaError && error.code === "UNSUPPORTED_CAPABILITY") {
console.error("Curated photos not available for this client");
}
}Guard with getCapabilities to avoid throwing in normal UI flows.
UNSUPPORTED_FILTER
Thrown when a filter key is not supported. Filters are never silently ignored — see Filters.
try {
await pixabay.searchPhotos({
query: "nature",
photoFilters: { size: "large" },
});
} catch (error) {
if (error instanceof MediaError && error.code === "UNSUPPORTED_FILTER") {
console.error(error.message);
// Filter "size" is not supported by this provider
}
}INVALID_FILTER_VALUE
Thrown when a filter key is supported but the value is invalid for that provider:
try {
await pexels.searchPhotos({
query: "nature",
photoFilters: { color: "grayscale" },
});
} catch (error) {
if (error instanceof MediaError && error.code === "INVALID_FILTER_VALUE") {
console.error(error.message);
// Invalid value "grayscale" for filter "color"
}
}HTTP errors
Provider API failures surface as MediaError with a status field:
try {
await client.getPhoto(999999999);
} catch (error) {
if (error instanceof MediaError && error.status === 404) {
console.error("Media not found");
}
}isAbortError
Cancelled search requests reject with an abort error, not MediaError:
import { isAbortError } from "@media-sdk/core";
try {
await client.searchPhotos({
query: "nature",
signal: controller.signal,
});
} catch (error) {
if (isAbortError(error)) {
return; // expected cancellation
}
throw error;
}See Cancellation for signal usage and React hook behavior.
Recommended error handler
import { isAbortError, MediaError } from "@media-sdk/core";
import type { MediaClient } from "@media-sdk/core";
async function safeSearch(
client: MediaClient,
query: string,
) {
try {
return await client.searchPhotos({
query,
page: 1,
perPage: 20,
});
} catch (error) {
if (isAbortError(error)) {
return null;
}
if (error instanceof MediaError) {
switch (error.code) {
case "UNSUPPORTED_FILTER":
throw new Error("Selected filters are not supported");
case "UNSUPPORTED_CAPABILITY":
throw new Error("This action is not supported");
case "INVALID_FILTER_VALUE":
throw new Error("Invalid filter selection");
default:
throw new Error(
`API error${error.status ? ` (${error.status})` : ""}: ${error.message}`,
);
}
}
throw error;
}
}import { isAbortError, MediaError } from "@media-sdk/core";
import { useMediaSearch } from "@media-sdk/react";
function SearchResults({ query }: { query: string }) {
const { data, error, loading } = useMediaSearch({
query,
page: 1,
perPage: 20,
enabled: query.length > 0,
});
if (loading) return <p>Loading…</p>;
if (error) {
if (error instanceof MediaError) {
if (error.code === "UNSUPPORTED_FILTER") {
return <p>Filters not supported by the active provider.</p>;
}
return <p>API error: {error.message}</p>;
}
return <p>Something went wrong.</p>;
}
// Aborts are not surfaced as error — hooks swallow them
return <p>{data?.items.length ?? 0} results</p>;
}Anti-patterns
// ❌ Do not branch on message text
if (error.message.includes("not supported")) { ... }
// ✅ Branch on code
if (error instanceof MediaError && error.code === "UNSUPPORTED_FILTER") { ... }// ❌ Do not assume all failures are MediaError
catch (error) {
console.log(error.status); // may be undefined
}
// ✅ Narrow the type first
catch (error) {
if (error instanceof MediaError) {
console.log(error.status, error.code);
}
}Related pages
- Error handling recipes — decision tree and hook examples
- Filters —
UNSUPPORTED_FILTERandINVALID_FILTER_VALUE - Capabilities —
UNSUPPORTED_CAPABILITY - Cancellation —
isAbortError - Migration 0.2→0.3 — adopting error codes in existing apps