Skip to Content
Examples & GuidesError Handling

Error Handling

Comprehensive guide to handling errors when using MarkdownAPI.io.

Error Response Format

All errors return JSON with a detail field:

{ "detail": "Descriptive error message" }

Validation errors (422) include field-level details:

{ "detail": [ { "loc": ["body", "name"], "msg": "field required", "type": "value_error.missing" } ] }

Common HTTP Status Codes

CodeMeaningTypical CauseAction
400Bad RequestInvalid inputValidate request data
401UnauthorizedInvalid/missing authCheck credentials
403ForbiddenInsufficient permissionsVerify ownership
404Not FoundResource doesn’t existCheck IDs
409ConflictDuplicate resourceUse unique names
413Payload Too LargeFile too largeReduce file size
422Unprocessable EntityValidation errorCheck field constraints
429Too Many RequestsRate limitedImplement backoff
500Internal Server ErrorServer errorRetry or contact support

Error Handling Patterns

Python Exception Handling

import httpx from typing import Optional class MarkdownAPIError(Exception): """Base exception for MarkdownAPI errors.""" pass class AuthenticationError(MarkdownAPIError): """Authentication failed.""" pass class NotFoundError(MarkdownAPIError): """Resource not found.""" pass class ConflictError(MarkdownAPIError): """Resource conflict (duplicate).""" pass async def upload_file_with_error_handling( project_id: str, file_path: str ) -> Optional[dict]: """Upload file with comprehensive error handling.""" try: return await upload_file(project_id, file_path) except httpx.HTTPStatusError as e: status = e.response.status_code if status == 401: raise AuthenticationError("Invalid API key") elif status == 404: raise NotFoundError(f"Project {project_id} not found") elif status == 409: raise ConflictError("File already exists") elif status == 429: # Rate limited - implement exponential backoff retry_after = int(e.response.headers.get("Retry-After", "60")) print(f"Rate limited. Retry after {retry_after}s") raise elif status >= 500: print(f"Server error: {status}") raise else: print(f"Unexpected error: {status}") raise except httpx.TimeoutException: print("Request timed out") raise except Exception as e: print(f"Unexpected error: {e}") raise

Retry Logic with Exponential Backoff

import asyncio from typing import TypeVar, Callable T = TypeVar('T') async def retry_with_backoff( func: Callable[..., T], max_retries: int = 3, initial_delay: float = 1.0, max_delay: float = 60.0, exponential_base: float = 2.0 ) -> T: """Retry function with exponential backoff.""" delay = initial_delay for attempt in range(max_retries): try: return await func() except httpx.HTTPStatusError as e: # Retry on rate limit or server errors if e.response.status_code in (429, 500, 502, 503, 504): if attempt < max_retries - 1: print(f"Attempt {attempt + 1} failed. Retrying in {delay}s...") await asyncio.sleep(delay) delay = min(delay * exponential_base, max_delay) else: raise else: # Don't retry client errors raise # Usage result = await retry_with_backoff( lambda: upload_file(project_id, file_path), max_retries=3 )

TypeScript Error Handling

class MarkdownAPIError extends Error { constructor( message: string, public statusCode: number, public response?: any ) { super(message); this.name = 'MarkdownAPIError'; } } async function uploadFileWithErrorHandling( projectId: string, filePath: string ): Promise<FileResponse> { try { const response = await fetch( `https://markdownapi.io/api/projects/${projectId}/files`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.MARKDOWN_API_KEY!}` }, body: createFormData(filePath), } ); if (!response.ok) { const error = await response.json(); switch (response.status) { case 401: throw new MarkdownAPIError('Invalid API key', 401); case 404: throw new MarkdownAPIError(`Project ${projectId} not found`, 404); case 409: throw new MarkdownAPIError('File already exists', 409, error); case 429: const retryAfter = response.headers.get('Retry-After') || '60'; throw new MarkdownAPIError(`Rate limited. Retry after ${retryAfter}s`, 429); default: throw new MarkdownAPIError( error.detail || 'Unknown error', response.status, error ); } } return await response.json(); } catch (error) { if (error instanceof MarkdownAPIError) { throw error; } throw new MarkdownAPIError('Network or unexpected error', 0, error); } }

Best Practices

  1. Specific exceptions: Catch specific errors, not generic Exception
  2. Retry transient errors: Retry 429, 500, 502, 503, 504
  3. Don’t retry client errors: Don’t retry 400, 401, 403, 404, 409
  4. Exponential backoff: Increase delay between retries
  5. Respect Retry-After: Honor Retry-After header on 429
  6. Log errors: Log errors with context for debugging
  7. User-friendly messages: Show user-friendly messages, not raw errors
  8. Timeout handling: Set appropriate timeouts and handle them

See Also

Last updated on