CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-notionhq--client

A simple and easy to use client for the Notion API

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Comprehensive error handling with specific error types and codes for different failure scenarios.

Capabilities

Error Types

The Notion SDK provides specific error classes for different types of failures.

/**
 * Union type of all possible errors from the Notion client
 */
type NotionClientError = 
  | APIResponseError
  | UnknownHTTPResponseError  
  | RequestTimeoutError;

/**
 * Check if an error is a Notion client error
 * @param error - Error to check
 * @returns True if error is from Notion client
 */
function isNotionClientError(error: unknown): error is NotionClientError;

API Response Errors

Errors returned from the Notion API with specific error codes.

/**
 * Error thrown when API returns an error response
 */
class APIResponseError extends Error {
  /** Error code from the API */
  code: APIErrorCode;
  /** HTTP status code */
  status: number;
  /** Additional error details */
  headers: Record<string, string>;
  /** Raw response body */
  body: string;
}

/**
 * Error codes returned by the Notion API
 */
enum APIErrorCode {
  Unauthorized = "unauthorized";
  RestrictedResource = "restricted_resource";
  ObjectNotFound = "object_not_found";
  RateLimited = "rate_limited";
  InvalidJSON = "invalid_json";
  InvalidRequestURL = "invalid_request_url";
  InvalidRequest = "invalid_request";
  ValidationError = "validation_error";
  ConflictError = "conflict_error";
  InternalServerError = "internal_server_error";
  ServiceUnavailable = "service_unavailable";
}

Usage Examples:

import { Client, APIErrorCode, isNotionClientError } from "@notionhq/client";

const notion = new Client({ auth: process.env.NOTION_TOKEN });

try {
  const page = await notion.pages.retrieve({
    page_id: "invalid-page-id",
  });
} catch (error) {
  if (isNotionClientError(error)) {
    console.log(`Notion error: ${error.code}`);
    
    if (error.code === APIErrorCode.ObjectNotFound) {
      console.log("Page not found");
    } else if (error.code === APIErrorCode.Unauthorized) {
      console.log("Invalid token or insufficient permissions");
    } else if (error.code === APIErrorCode.RateLimited) {
      console.log("Rate limited - retry after delay");
    }
  } else {
    console.log("Unknown error:", error);
  }
}

Client Errors

Errors generated by the client itself, not the API.

/**
 * Error codes for client-side errors
 */
enum ClientErrorCode {
  RequestTimeout = "notionhq_client_request_timeout";
  ResponseError = "notionhq_client_response_error";
}

/**
 * Error thrown when request times out
 */
class RequestTimeoutError extends Error {
  code: ClientErrorCode.RequestTimeout;
}

/**
 * Error thrown for unknown HTTP response errors
 */
class UnknownHTTPResponseError extends Error {
  code: ClientErrorCode.ResponseError;
  /** HTTP status code */
  status: number;
  /** Response body */
  body: string;
}

Usage Examples:

import { Client, ClientErrorCode } from "@notionhq/client";

const notion = new Client({ 
  auth: process.env.NOTION_TOKEN,
  timeoutMs: 5000, // 5 second timeout
});

try {
  const page = await notion.pages.retrieve({
    page_id: "page-id",
  });
} catch (error) {
  if (isNotionClientError(error)) {
    if (error.code === ClientErrorCode.RequestTimeout) {
      console.log("Request timed out - try again");
    } else if (error.code === ClientErrorCode.ResponseError) {
      console.log(`HTTP error ${error.status}: ${error.body}`);
    }
  }
}

Error Code Union Type

/**
 * Union of all error codes (API + Client)
 */
type NotionErrorCode = APIErrorCode | ClientErrorCode;

Error Handling Patterns

Basic Error Handling

async function safePage Retrieval(pageId: string) {
  try {
    const page = await notion.pages.retrieve({ page_id: pageId });
    return { success: true, page };
  } catch (error) {
    if (isNotionClientError(error)) {
      return { 
        success: false, 
        error: error.code,
        message: error.message 
      };
    }
    
    return { 
      success: false, 
      error: "unknown", 
      message: "Unknown error occurred" 
    };
  }
}

Retry with Exponential Backoff

async function retryWithBackoff<T>(
  operation: () => Promise<T>,
  maxRetries: number = 3
): Promise<T> {
  let lastError: Error;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      lastError = error as Error;
      
      if (isNotionClientError(error)) {
        // Don't retry certain errors
        if (error.code === APIErrorCode.Unauthorized ||
            error.code === APIErrorCode.ObjectNotFound ||
            error.code === APIErrorCode.InvalidRequest) {
          throw error;
        }
        
        // Retry rate limited or server errors
        if (error.code === APIErrorCode.RateLimited ||
            error.code === APIErrorCode.InternalServerError ||
            error.code === APIErrorCode.ServiceUnavailable ||
            error.code === ClientErrorCode.RequestTimeout) {
          
          const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }
      }
      
      throw error;
    }
  }
  
  throw lastError!;
}

// Usage
const page = await retryWithBackoff(() => 
  notion.pages.retrieve({ page_id: "page-id" })
);

Detailed Error Information

function handleNotionError(error: unknown) {
  if (!isNotionClientError(error)) {
    console.log("Non-Notion error:", error);
    return;
  }
  
  console.log(`Error Code: ${error.code}`);
  console.log(`Error Message: ${error.message}`);
  
  if (error instanceof APIResponseError) {
    console.log(`HTTP Status: ${error.status}`);
    console.log(`Response Headers:`, error.headers);
    console.log(`Response Body:`, error.body);
  }
  
  if (error instanceof UnknownHTTPResponseError) {
    console.log(`HTTP Status: ${error.status}`);
    console.log(`Response Body:`, error.body);
  }
  
  // Handle specific error codes
  switch (error.code) {
    case APIErrorCode.Unauthorized:
      console.log("Check your API token and permissions");
      break;
    case APIErrorCode.RateLimited:
      console.log("Slow down requests to avoid rate limiting");
      break;
    case APIErrorCode.ObjectNotFound:
      console.log("The requested resource was not found");
      break;
    case APIErrorCode.ValidationError:
      console.log("Request data failed validation");
      break;
    case ClientErrorCode.RequestTimeout:
      console.log("Request timed out - consider increasing timeout");
      break;
  }
}

Graceful Degradation

async function getPageOrFallback(pageId: string) {
  try {
    return await notion.pages.retrieve({ page_id: pageId });
  } catch (error) {
    if (isNotionClientError(error)) {
      if (error.code === APIErrorCode.ObjectNotFound) {
        // Return a default page structure
        return {
          object: "page" as const,
          id: pageId,
          properties: {},
          // ... other default properties
        };
      }
      
      if (error.code === APIErrorCode.Unauthorized) {
        throw new Error("Authentication required");
      }
    }
    
    // Re-throw other errors
    throw error;
  }
}

Types

enum APIErrorCode {
  Unauthorized = "unauthorized";
  RestrictedResource = "restricted_resource";
  ObjectNotFound = "object_not_found";
  RateLimited = "rate_limited";
  InvalidJSON = "invalid_json";
  InvalidRequestURL = "invalid_request_url";
  InvalidRequest = "invalid_request";
  ValidationError = "validation_error";
  ConflictError = "conflict_error";
  InternalServerError = "internal_server_error";
  ServiceUnavailable = "service_unavailable";
}

enum ClientErrorCode {
  RequestTimeout = "notionhq_client_request_timeout";
  ResponseError = "notionhq_client_response_error";
}

type NotionErrorCode = APIErrorCode | ClientErrorCode;

type NotionClientError = APIResponseError | UnknownHTTPResponseError | RequestTimeoutError;

class APIResponseError extends Error {
  code: APIErrorCode;
  status: number;
  headers: Record<string, string>;
  body: string;
}

class UnknownHTTPResponseError extends Error {
  code: ClientErrorCode.ResponseError;
  status: number;
  body: string;
}

class RequestTimeoutError extends Error {
  code: ClientErrorCode.RequestTimeout;
}

function isNotionClientError(error: unknown): error is NotionClientError;

Install with Tessl CLI

npx tessl i tessl/npm-notionhq--client

docs

block-operations.md

client-configuration.md

comments.md

data-source-operations.md

database-operations.md

error-handling.md

file-uploads.md

index.md

oauth-authentication.md

page-operations.md

pagination-helpers.md

search.md

user-management.md

tile.json