Error Handling
The SDK maps every non-2xx API response to a typed error class. All errors
extend StudyPlugError, and the SDK exports type guard functions so you can
handle each case cleanly without instanceof checks.
Error Class Hierarchy
Section titled “Error Class Hierarchy”StudyPlugError (base — any non-2xx response) ├── AuthenticationError (401 / 403) ├── RateLimitError (429) ├── NotFoundError (404) └── ValidationError (422)StudyPlugError (Base)
Section titled “StudyPlugError (Base)”Every SDK error has these properties:
class StudyPlugError extends Error { readonly status: number; // HTTP status code (0 for network/timeout) readonly raw: ApiErrorBody; // Full RFC 7807 Problem Details body}The raw object contains the complete error response:
interface ApiErrorBody { type: string; // error type URI title: string; // short description status: number; // HTTP status code detail: string; // human-readable explanation instance: string; // request URL errors?: ValidationErrorDetail[]; retryAfter?: number;}AuthenticationError
Section titled “AuthenticationError”Thrown on 401 or 403 responses. Indicates a missing or invalid API key.
import { isAuthenticationError } from "studyplug";
try { await sp.skills.list();} catch (err) { if (isAuthenticationError(err)) { console.log(err.status); // 401 console.log(err.message); // "Invalid API key" }}RateLimitError
Section titled “RateLimitError”Thrown on 429 responses after all automatic retries are exhausted. The SDK
retries 429s automatically (default: 2 retries with Retry-After backoff)
before throwing.
import { isRateLimitError } from "studyplug";
try { await sp.generate({ skill: "add-within-10", count: 50 });} catch (err) { if (isRateLimitError(err)) { console.log(err.retryAfter); // seconds until you can retry console.log(err.status); // 429 }}| Property | Type | Description |
|---|---|---|
retryAfter | number | Seconds to wait before retrying (from Retry-After header or response body, defaults to 60) |
NotFoundError
Section titled “NotFoundError”Thrown on 404 responses. A requested resource does not exist.
import { isNotFoundError } from "studyplug";
try { await sp.skills.get("nonexistent-skill");} catch (err) { if (isNotFoundError(err)) { console.log(err.message); // "Skill 'nonexistent-skill' not found" }}ValidationError
Section titled “ValidationError”Thrown on 422 responses. The request body or query parameters failed validation.
import { isValidationError } from "studyplug";
try { await sp.generate({ count: 999 });} catch (err) { if (isValidationError(err)) { for (const fieldError of err.fieldErrors) { console.log(`${fieldError.field}: ${fieldError.message}`); // "count: Number must be less than or equal to 50" } }}| Property | Type | Description |
|---|---|---|
fieldErrors | ValidationErrorDetail[] | Array of { field, message, value? } |
Network and Timeout Errors
Section titled “Network and Timeout Errors”Network failures and timeouts also throw StudyPlugError with status: 0:
import { isStudyPlugError } from "studyplug";
try { await sp.health();} catch (err) { if (isStudyPlugError(err) && err.status === 0) { console.log(err.message); // "Request timed out after 30000ms" // or "Failed to fetch" (network error) }}Type Guards
Section titled “Type Guards”Use type guards instead of instanceof for cleaner code and better tree-shaking:
import { isStudyPlugError, isAuthenticationError, isRateLimitError, isNotFoundError, isValidationError,} from "studyplug";| Guard | Matches |
|---|---|
isStudyPlugError(err) | Any SDK error |
isAuthenticationError(err) | 401 / 403 |
isRateLimitError(err) | 429 |
isNotFoundError(err) | 404 |
isValidationError(err) | 422 |
Recommended Pattern
Section titled “Recommended Pattern”import { isRateLimitError, isNotFoundError, isValidationError, isStudyPlugError,} from "studyplug";
try { const { data } = await sp.generate({ skill: "add-within-10", count: 5 }); // use data...} catch (err) { if (isRateLimitError(err)) { // Back off and retry await sleep(err.retryAfter * 1000); } else if (isNotFoundError(err)) { // Skill does not exist console.error("Unknown skill"); } else if (isValidationError(err)) { // Bad request parameters console.error(err.fieldErrors); } else if (isStudyPlugError(err)) { // Other API error (500, etc.) console.error(`API error ${err.status}: ${err.message}`); } else { throw err; // Not from StudyPlug SDK }}