Error handling recipes
Use this page when wiring try/catch blocks or hook error state. For the full MediaError reference, see Errors.
Decision tree
mermaid
flowchart TD
start["catch (error)"]
abort{"isAbortError(error)?"}
media{"error instanceof MediaError?"}
code{"error.code set?"}
status{"error.status set?"}
ignore["Ignore — expected cancellation"]
codeBranch["Branch on code:\nUNSUPPORTED_CAPABILITY\nUNSUPPORTED_FILTER\nINVALID_FILTER_VALUE"]
statusBranch["Branch on status:\n401/403 auth\n429 rate limit\n404 not found\n5xx server"]
generic["Generic Error:\nnetwork, parse, missing API key"]
start --> abort
abort -->|yes| ignore
abort -->|no| media
media -->|yes| code
media -->|no| generic
code -->|yes| codeBranch
code -->|no| status
status -->|yes| statusBranch
status -->|no| generic| Step | Check | Action |
|---|---|---|
| 1 | isAbortError(error) | Return early — hooks already swallow aborts in UI state |
| 2 | error instanceof MediaError && error.code | Map to user-facing copy (capability, filter, invalid value) |
| 3 | error instanceof MediaError && error.status | Map to auth, rate-limit, not-found, or server messages |
| 4 | Plain Error | Treat as network failure, JSON parse error, or unexpected failure |
Import helpers from @media-sdk/core:
ts
import { isAbortError, MediaError } from "@media-sdk/core";Core try/catch template
ts
import { isAbortError, MediaError } from "@media-sdk/core";
async function searchWithHandling(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_CAPABILITY":
throw new Error("This action is not available for the selected provider.");
case "UNSUPPORTED_FILTER":
throw new Error("One or more filters are not supported.");
case "INVALID_FILTER_VALUE":
throw new Error("Invalid filter selection.");
default:
break;
}
if (error.status === 401 || error.status === 403) {
throw new Error("Check your API key and provider permissions.");
}
if (error.status === 429) {
throw new Error("Rate limit exceeded — try again later.");
}
if (error.status && error.status >= 500) {
throw new Error("Provider API is temporarily unavailable.");
}
throw new Error(error.message);
}
// Network, JSON parse, missing ApiKeyProvider key, etc.
throw error instanceof Error ? error : new Error(String(error));
}
}MediaError codes (copy-paste)
UNSUPPORTED_CAPABILITY
Thrown when the active client does not implement an operation (for example curated photos on Pixabay):
ts
import { MediaError } from "@media-sdk/core";
try {
await client.getCuratedPhotos({ page: 1, perPage: 20 });
} catch (error) {
if (error instanceof MediaError && error.code === "UNSUPPORTED_CAPABILITY") {
console.error("Curated photos are not available for this provider");
}
}Prefer guarding with getCapabilities so this path is rare.
UNSUPPORTED_FILTER
Thrown when a filter key is not supported — filters are never silently ignored:
ts
try {
await client.searchPhotos({
query: "nature",
photoFilters: { size: "large" },
});
} catch (error) {
if (error instanceof MediaError && error.code === "UNSUPPORTED_FILTER") {
console.error("The size filter is not supported by this provider");
}
}INVALID_FILTER_VALUE
Thrown when the filter key is supported but the value is invalid:
ts
try {
await client.searchPhotos({
query: "nature",
photoFilters: { color: "grayscale" },
});
} catch (error) {
if (error instanceof MediaError && error.code === "INVALID_FILTER_VALUE") {
console.error("That color value is not valid for this provider");
}
}Hook error surface
useMediaSearch and useMediaVideos expose error: Error | null. At runtime, failures from core are often MediaError instances — narrow with instanceof before reading code or status:
tsx
import { MediaError } from "@media-sdk/core";
import { useMediaSearch } from "@media-sdk/react";
function SearchResults({ query }: { query: string }) {
const { data, loading, error } = useMediaSearch({ query });
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>;
}
if (error.status === 401 || error.status === 403) {
return <p>Invalid API key — check your environment variables.</p>;
}
if (error.status === 429) {
return <p>Rate limit exceeded. Please wait and try again.</p>;
}
return <p>{error.message}</p>;
}
return <p>Something went wrong. Check your network connection.</p>;
}
return <p>{data?.items.length ?? 0} results</p>;
}Aborted requests do not populate error. See Cancellation.
Anti-patterns
ts
// ❌ Do not parse error.message for control flow
if (error.message.includes("not supported")) { ... }
// ✅ Branch on error.code or error.status
if (error instanceof MediaError && error.code === "UNSUPPORTED_FILTER") { ... }Related pages
- Errors — full taxonomy and HTTP behavior
- React hooks — error handling
- Native filters — error handling
- Cancellation —
isAbortErrorand hook abort behavior - Capabilities — avoid
UNSUPPORTED_CAPABILITYin UI