Error Handling
The SDK has 4 error classes. Each class handles a different type of failure.
Table of Contents
Section titled “Table of Contents”- Error Types
- NotionValidationError
- NotionAPIError
- NotionNetworkError
- NotionRequestTimeoutError
- Error Handling Patterns
- Error Codes Reference
Error Types
Section titled “Error Types”Each SDK error class extends the built-in Error class:
import { NotionValidationError, // Client-side validation failures NotionAPIError, // API responses with error status NotionNetworkError, // Network or connection failures NotionRequestTimeoutError, // Request timeout} from '@visus-io/notion-sdk-ts';NotionValidationError
Section titled “NotionValidationError”The SDK throws NotionValidationError when client-side validation fails. This check happens
before the SDK sends a request to the API.
When the SDK Throws This Error
Section titled “When the SDK Throws This Error”- Rich text content exceeds 2,000 characters.
- A URL exceeds 2,000 characters.
- An array exceeds 100 elements.
- The input data is invalid.
Properties
Section titled “Properties”error.message; // Description of validation failureExample
Section titled “Example”import { NotionValidationError, richText } from '@visus-io/notion-sdk-ts';
try { // Text exceeds 2,000 character limit const text = 'a'.repeat(3000); richText(text).build();} catch (error) { if (error instanceof NotionValidationError) { console.error('Validation error:', error.message); // "Rich text content exceeds maximum length of 2000 characters" }}Handling Validation Errors
Section titled “Handling Validation Errors”try { await notion.blocks.children.append('page-id', { children: Array(150).fill(block.paragraph('Too many blocks')), });} catch (error) { if (error instanceof NotionValidationError) { console.error('Request validation failed:'); console.error(error.message); // Split into smaller batches }}See Request Size Limits for all enforced limits.
NotionAPIError
Section titled “NotionAPIError”The SDK throws NotionAPIError when the Notion API returns an error response. The response has
a 4xx or 5xx status code.
Properties
Section titled “Properties”error.status; // HTTP status code (e.g., 404, 401, 429)error.code; // Notion error code (e.g., 'object_not_found')error.message; // Error message from APIerror.retryAfterMs; // Wait time before retry, in milliseconds (rate-limited and overload responses)error.rateLimitReason; // Why the API rate-limited the request (rate-limited responses only)Type Guards
Section titled “Type Guards”error.isNotFound(); // 404: resource not founderror.isUnauthorized(); // 401: invalid token or missing permissionserror.isValidationError(); // 400: invalid request dataerror.isRateLimited(); // 429: rate limit exceedederror.isServiceOverloaded(); // 529: Notion is overloadederror.isServerError(); // 5xx: Notion server error, includes 529error.isRetryable(); // rate limited or server errorRetry behavior: The SDK retries every error that isRetryable() reports. It retries
rate-limited (429) responses only when retryOnRateLimit is enabled. It always retries
service-overload (529) responses and transient server errors (500 to 599), no matter the value
of retryOnRateLimit. See Configuration. The Notion API recommends
this retry behavior.
Exception: notion.pages.updateMarkdown() does not retry a 504 gateway_timeout
automatically, unlike other transient server errors. A 504 does not guarantee that the write
failed, so check the page content before you retry.
Rate limit reason: A rate-limited response may add a rate_limit_reason value to
additional_data. Read it from error.rateLimitReason. Notion documents these values:
public_api_request_rate_limit— a per-connection limitpublic_api_space_request_rate_limit— a shared workspace limitpublic_api_endpoint_rate_limit— a limit on one endpointmcp_tool_rate_limitcollection_router_upstream_429public_api_request_blocked
Notion may add new values over time. Treat an unrecognized value the same as any other
rate_limited response. error.rateLimitReason still holds the string. isRetryable() still
returns its normal result.
A public_api_request_blocked request cannot succeed on retry. isRetryable() returns false
for this reason, so the SDK does not retry it automatically.
Example
Section titled “Example”import { NotionAPIError } from '@visus-io/notion-sdk-ts';
try { await notion.pages.retrieve('invalid-page-id');} catch (error) { if (error instanceof NotionAPIError) { console.error(`API Error ${error.status}:`, error.message); console.error('Error code:', error.code);
if (error.isNotFound()) { console.error('Page does not exist or bot lacks access'); } else if (error.isUnauthorized()) { console.error('Invalid token or missing permissions'); } else if (error.isRateLimited()) { console.error('Rate limited, retry after delay'); } }}Handling Specific Error Codes
Section titled “Handling Specific Error Codes”try { await notion.databases.query('database-id');} catch (error) { if (error instanceof NotionAPIError) { switch (error.code) { case 'object_not_found': console.error('Database not found'); break; case 'unauthorized': console.error('Token invalid or database not shared'); break; case 'rate_limited': console.error('Rate limited, wait before retrying'); break; case 'validation_error': console.error('Invalid filter or sort parameters'); break; default: console.error('API error:', error.message); } }}NotionNetworkError
Section titled “NotionNetworkError”The SDK throws NotionNetworkError when a network-level error occurs. Examples: a DNS failure
or a refused connection.
Properties
Section titled “Properties”error.message; // Error descriptionerror.cause; // Underlying error objectWhen the SDK Throws This Error
Section titled “When the SDK Throws This Error”- The DNS lookup fails.
- The connection is refused.
- The network request times out.
- A TLS or SSL error occurs.
Example
Section titled “Example”import { NotionNetworkError } from '@visus-io/notion-sdk-ts';
try { await notion.pages.retrieve('page-id');} catch (error) { if (error instanceof NotionNetworkError) { console.error('Network error:', error.message); console.error('Cause:', error.cause); // Retry with exponential backoff or alert monitoring }}NotionRequestTimeoutError
Section titled “NotionRequestTimeoutError”The SDK throws NotionRequestTimeoutError when a request exceeds the configured timeout.
Properties
Section titled “Properties”error.message; // Timeout descriptionExample
Section titled “Example”import { NotionRequestTimeoutError } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN, timeoutMs: 5_000, // 5 second timeout});
try { await notion.databases.query('large-database-id');} catch (error) { if (error instanceof NotionRequestTimeoutError) { console.error('Request timed out:', error.message); // Increase timeout or optimize query }}Error Handling Patterns
Section titled “Error Handling Patterns”Comprehensive Error Handler
Section titled “Comprehensive Error Handler”import { NotionAPIError, NotionNetworkError, NotionRequestTimeoutError, NotionValidationError,} from '@visus-io/notion-sdk-ts';
async function safeRetrievePage(pageId: string) { try { return await notion.pages.retrieve(pageId); } catch (error) { if (error instanceof NotionValidationError) { console.error('Validation failed:', error.message); throw new Error('Invalid input data'); } else if (error instanceof NotionAPIError) { console.error(`API Error ${error.status}:`, error.message);
if (error.isNotFound()) { return null; // Page does not exist } else if (error.isUnauthorized()) { throw new Error('Authentication failed'); } else if (error.isRateLimited()) { // Wait and retry await new Promise((resolve) => setTimeout(resolve, 5000)); return safeRetrievePage(pageId); // Retry once } else if (error.isServerError()) { throw new Error('Notion server error, try again later'); }
throw error; } else if (error instanceof NotionNetworkError) { console.error('Network error:', error.message); throw new Error('Network connection failed'); } else if (error instanceof NotionRequestTimeoutError) { console.error('Request timed out:', error.message); throw new Error('Request took too long'); }
throw error; // Unknown error }}Retry Pattern
Section titled “Retry Pattern”async function retryWithBackoff<T>( fn: () => Promise<T>, maxRetries = 3, baseDelay = 1000,): Promise<T> { let lastError: Error | undefined;
for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await fn(); } catch (error) { lastError = error as Error;
// Only retry on retryable errors if (error instanceof NotionAPIError && (error.isRateLimited() || error.isServerError())) { if (attempt < maxRetries) { const delay = baseDelay * Math.pow(2, attempt); console.log(`Retry attempt ${attempt + 1} after ${delay}ms`); await new Promise((resolve) => setTimeout(resolve, delay)); continue; } }
throw error; // Do not retry other errors } }
throw lastError;}
// Usageconst page = await retryWithBackoff(() => notion.pages.retrieve('page-id'));Graceful Degradation
Section titled “Graceful Degradation”async function getPageTitle(pageId: string): Promise<string> { try { const page = await notion.pages.retrieve(pageId); return page.getTitle(); } catch (error) { if (error instanceof NotionAPIError && error.isNotFound()) { return 'Untitled'; // Default value } throw error; // Re-throw other errors }}Bulk Operation Error Handling
Section titled “Bulk Operation Error Handling”async function bulkUpdatePages(pageIds: string[]) { const results = { success: [] as string[], failed: [] as { id: string; error: string }[], };
for (const pageId of pageIds) { try { await notion.pages.update(pageId, { properties: { Status: { status: { name: 'Done' } } }, }); results.success.push(pageId); } catch (error) { let errorMessage = 'Unknown error';
if (error instanceof NotionAPIError) { errorMessage = `${error.code}: ${error.message}`; } else if (error instanceof NotionNetworkError) { errorMessage = 'Network error'; }
results.failed.push({ id: pageId, error: errorMessage }); } }
console.log(`Success: ${results.success.length}, Failed: ${results.failed.length}`); return results;}Error Codes Reference
Section titled “Error Codes Reference”This table lists the Notion API error codes:
| Code | Status | Description |
|---|---|---|
invalid_json |
400 | Request body is not valid JSON |
invalid_request_url |
400 | Invalid request URL |
invalid_request |
400 | Request is invalid |
validation_error |
400 | Validation failed on request parameters |
missing_version |
400 | Notion-Version header missing |
unauthorized |
401 | Invalid token or insufficient permissions |
restricted_resource |
403 | Forbidden access to resource |
object_not_found |
404 | Resource not found |
conflict_error |
409 | Conflict with existing resource state |
rate_limited |
429 | Too many requests (retried if retryOnRateLimit is enabled) |
internal_server_error |
500 | Notion internal error (always auto-retried) |
service_unavailable |
503 | Service temporarily unavailable (always auto-retried) |
database_connection_unavailable |
503 | Database connection error (always auto-retried) |
gateway_timeout |
504 | Gateway timeout (always auto-retried, except updateMarkdown()) |
service_overload |
529 | Notion is overloaded (always auto-retried) |
Notion may add new codes over time. Treat an unrecognized code the same as any other error
response. error.code still holds the string. Read error.status to classify the error.
Common Error Scenarios
Section titled “Common Error Scenarios”404 Not Found (object_not_found):
- The page or database does not exist.
- The bot does not have access to the resource.
- Someone deleted the resource.
401 Unauthorized (unauthorized):
- The integration token is invalid.
- The token expired.
- The integration is not connected to the page or database.
400 Validation Error (validation_error):
- The property type is invalid.
- The filter syntax is invalid.
- A required field is missing.
429 Rate Limited (rate_limited):
- Too many requests occurred in a short time.
- The SDK retries the request automatically. You can configure this behavior.
500 and 503 Server Errors:
- A temporary Notion service issue occurred.
- The SDK retries the request automatically for retryable operations.
Related Pages
Section titled “Related Pages”- Configuration: how to configure retries and timeouts.
- Request Size Limits: client-side validation limits.
- Common Use Cases: error handling in practice.
- API Reference: API method documentation.