> ## Documentation Index
> Fetch the complete documentation index at: https://paystack-sdk.efobi.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Comprehensive guide to handling errors in the Paystack TypeScript SDK

The Paystack SDK uses a unique error handling pattern that combines TypeScript's type safety with Zod's runtime validation. Unlike traditional try-catch approaches, methods return a result object that contains either data or an error.

## The Result Pattern

All SDK methods return a result object with two properties:

```typescript theme={null}
type Result<T> = {
  data: T | undefined;
  error: ZodError | undefined;
}
```

<Note>
  Either `data` or `error` will be defined, never both. This pattern eliminates the need for try-catch blocks in most cases.
</Note>

### Basic Error Handling

Here's how to handle responses from the SDK:

```typescript theme={null}
import { Paystack } from '@efobi/paystack';

const paystack = new Paystack(process.env.PAYSTACK_SECRET_KEY!);

const result = await paystack.transaction.verify('invalid_reference');

if (result.error) {
  // Handle validation or API error
  console.error('Error:', result.error.message);
  console.error('Issues:', result.error.issues);
} else if (result.data) {
  // Success - use the data
  console.log('Transaction:', result.data.data);
  console.log('Status:', result.data.status);
}
```

## Types of Errors

The SDK handles three main categories of errors:

<CardGroup cols={3}>
  <Card title="Validation Errors" icon="clipboard-check">
    Input data fails Zod schema validation before the API request
  </Card>

  <Card title="API Errors" icon="cloud-exclamation">
    Paystack API returns an error response (4xx, 5xx status codes)
  </Card>

  <Card title="Network Errors" icon="wifi-slash">
    Connection issues, timeouts, or network failures
  </Card>
</CardGroup>

### 1. Validation Errors

Zod validates all inputs before making API requests. If validation fails, you'll receive a detailed error:

```typescript theme={null}
const result = await paystack.transaction.initialize({
  email: 'invalid-email',  // ❌ Not a valid email
  amount: '50000'
});

if (result.error) {
  console.log(result.error.issues);
  // [
  //   {
  //     code: 'invalid_string',
  //     validation: 'email',
  //     path: ['email'],
  //     message: 'Invalid email'
  //   }
  // ]
}
```

#### Validation Error Structure

Zod errors provide detailed information:

```typescript theme={null}
interface ZodIssue {
  code: string;           // Error type (e.g., 'invalid_string', 'too_small')
  path: (string | number)[]; // Path to the invalid field
  message: string;        // Human-readable error message
  validation?: string;    // Validation type (e.g., 'email', 'url')
  expected?: string;      // Expected value/type
  received?: string;      // Actual value/type received
}
```

<CodeGroup>
  ```typescript Flatten Errors theme={null}
  // Get user-friendly error messages
  const result = await paystack.transaction.initialize(invalidData);

  if (result.error) {
    const flatErrors = result.error.flatten();
    console.log(flatErrors.fieldErrors);
    // {
    //   email: ['Invalid email'],
    //   amount: ['Required']
    // }
  }
  ```

  ```typescript Format Errors theme={null}
  // Format for API responses
  const result = await paystack.transaction.initialize(invalidData);

  if (result.error) {
    const formatted = result.error.format();
    console.log(formatted);
    // {
    //   email: { _errors: ['Invalid email'] },
    //   amount: { _errors: ['Required'] }
    // }
  }
  ```

  ```typescript Extract Messages theme={null}
  // Get all error messages as array
  const result = await paystack.transaction.initialize(invalidData);

  if (result.error) {
    const messages = result.error.issues.map(issue => issue.message);
    console.log(messages);
    // ['Invalid email', 'Required']
  }
  ```
</CodeGroup>

### 2. API Errors

When Paystack returns an error response, the SDK parses it and returns the error in the result. Here's the implementation from `src/main/transaction.ts:119-124`:

```typescript src/main/transaction.ts theme={null}
if (!response.ok) {
  const { data, error } = await genericResponse.safeParseAsync(raw);
  return { data, error };
}
const { data, error } = await txnInitializeSuccess.safeParseAsync(raw);
return { data, error };
```

#### Common API Errors

<AccordionGroup>
  <Accordion title="400 Bad Request">
    The request contains invalid parameters.

    ```typescript theme={null}
    const result = await paystack.transaction.initialize({
      email: 'customer@example.com',
      amount: '-1000'  // ❌ Negative amount
    });

    if (result.error) {
      // API validation failed
      console.error('Bad request:', result.error.message);
    }
    ```
  </Accordion>

  <Accordion title="401 Unauthorized">
    Invalid or missing API key.

    ```typescript theme={null}
    // Using invalid key
    const paystack = new Paystack('sk_test_invalid_key');
    const result = await paystack.transaction.list();

    if (result.error) {
      // Unauthorized - check your API key
      console.error('Authentication failed');
    }
    ```
  </Accordion>

  <Accordion title="404 Not Found">
    The requested resource doesn't exist.

    ```typescript theme={null}
    const result = await paystack.transaction.verify('non_existent_ref');

    if (result.error) {
      // Transaction not found
      console.error('Resource not found');
    }
    ```
  </Accordion>

  <Accordion title="429 Too Many Requests">
    Rate limit exceeded.

    ```typescript theme={null}
    const result = await paystack.transaction.list();

    if (result.error) {
      // Too many requests - implement backoff
      console.error('Rate limited - retry after delay');
    }
    ```
  </Accordion>

  <Accordion title="500 Internal Server Error">
    Paystack server error (rare).

    ```typescript theme={null}
    const result = await paystack.transaction.initialize(data);

    if (result.error) {
      // Server error - retry or contact support
      console.error('Server error - please try again');
    }
    ```
  </Accordion>
</AccordionGroup>

### 3. Network Errors

Network errors occur outside the SDK's control and should be caught with try-catch:

```typescript theme={null}
try {
  const result = await paystack.transaction.verify(reference);
  
  if (result.error) {
    // Validation or API error
    handleApiError(result.error);
  } else if (result.data) {
    // Success
    processTransaction(result.data);
  }
} catch (error) {
  // Network error: connection timeout, DNS failure, etc.
  console.error('Network error:', error.message);
  // Implement retry logic or show user-friendly message
}
```

## Error Handling Patterns

### Pattern 1: Simple Check

For basic operations where you just need to know if it succeeded:

```typescript theme={null}
const result = await paystack.transaction.initialize({
  email: 'customer@example.com',
  amount: '50000'
});

if (result.error) {
  return { success: false, message: 'Failed to initialize transaction' };
}

return { success: true, url: result.data.data.authorization_url };
```

### Pattern 2: Detailed Error Handling

When you need granular control:

```typescript theme={null}
const result = await paystack.transaction.verify(reference);

if (result.error) {
  // Log full error for debugging
  console.error('Verification failed:', result.error);
  
  // Extract user-friendly messages
  const errorMessages = result.error.issues.map(issue => ({
    field: issue.path.join('.'),
    message: issue.message
  }));
  
  return {
    success: false,
    errors: errorMessages
  };
}

// Success path
const transaction = result.data.data;
return {
  success: true,
  status: transaction.status,
  amount: transaction.amount
};
```

### Pattern 3: Type Guards

Use TypeScript type guards for safer code:

```typescript theme={null}
function isError<T>(result: { data: T | undefined; error: any }): result is { error: any } {
  return result.error !== undefined;
}

const result = await paystack.transaction.initialize(data);

if (isError(result)) {
  // TypeScript knows result.error exists
  throw new Error(`Transaction failed: ${result.error.message}`);
}

// TypeScript knows result.data exists here
const { authorization_url } = result.data.data;
```

### Pattern 4: Centralized Error Handler

Create a reusable error handler:

```typescript theme={null}
function handlePaystackError(error: any) {
  const issues = error.issues || [];
  
  return {
    message: 'Operation failed',
    errors: issues.map((issue: any) => ({
      field: issue.path.join('.') || 'general',
      message: issue.message,
      code: issue.code
    }))
  };
}

// Usage
const result = await paystack.transaction.list();

if (result.error) {
  const formatted = handlePaystackError(result.error);
  return res.status(400).json(formatted);
}

return res.json(result.data);
```

## Framework Integration

<Tabs>
  <Tab title="Express.js">
    ```typescript theme={null}
    import express from 'express';
    import { Paystack } from '@efobi/paystack';

    const app = express();
    const paystack = new Paystack(process.env.PAYSTACK_SECRET_KEY!);

    app.post('/api/transactions/verify/:reference', async (req, res) => {
      try {
        const result = await paystack.transaction.verify(req.params.reference);
        
        if (result.error) {
          return res.status(400).json({
            success: false,
            message: 'Verification failed',
            errors: result.error.flatten().fieldErrors
          });
        }
        
        return res.json({
          success: true,
          data: result.data.data
        });
      } catch (error) {
        console.error('Network error:', error);
        return res.status(500).json({
          success: false,
          message: 'Internal server error'
        });
      }
    });
    ```
  </Tab>

  <Tab title="Next.js App Router">
    ```typescript theme={null}
    // app/api/transactions/verify/[reference]/route.ts
    import { NextResponse } from 'next/server';
    import { Paystack } from '@efobi/paystack';

    const paystack = new Paystack(process.env.PAYSTACK_SECRET_KEY!);

    export async function GET(
      request: Request,
      { params }: { params: { reference: string } }
    ) {
      try {
        const result = await paystack.transaction.verify(params.reference);
        
        if (result.error) {
          return NextResponse.json(
            {
              success: false,
              message: 'Verification failed',
              errors: result.error.issues
            },
            { status: 400 }
          );
        }
        
        return NextResponse.json({
          success: true,
          data: result.data.data
        });
      } catch (error) {
        console.error('Network error:', error);
        return NextResponse.json(
          { success: false, message: 'Internal server error' },
          { status: 500 }
        );
      }
    }
    ```
  </Tab>

  <Tab title="Hono">
    ```typescript theme={null}
    import { Hono } from 'hono';
    import { Paystack } from '@efobi/paystack';

    const app = new Hono();
    const paystack = new Paystack(process.env.PAYSTACK_SECRET_KEY!);

    app.get('/api/transactions/verify/:reference', async (c) => {
      try {
        const reference = c.req.param('reference');
        const result = await paystack.transaction.verify(reference);
        
        if (result.error) {
          return c.json(
            {
              success: false,
              message: 'Verification failed',
              errors: result.error.issues
            },
            400
          );
        }
        
        return c.json({
          success: true,
          data: result.data.data
        });
      } catch (error) {
        console.error('Network error:', error);
        return c.json(
          { success: false, message: 'Internal server error' },
          500
        );
      }
    });
    ```
  </Tab>
</Tabs>

## Best Practices

<CardGroup cols={2}>
  <Card title="Always Check Errors First" icon="1">
    Check for `result.error` before accessing `result.data` to avoid undefined errors.

    ```typescript theme={null}
    // ✅ Good
    if (result.error) return;
    console.log(result.data);

    // ❌ Bad
    console.log(result.data); // May be undefined
    ```
  </Card>

  <Card title="Log Errors Completely" icon="2">
    Log the full error object for debugging, but show user-friendly messages to users.

    ```typescript theme={null}
    if (result.error) {
      console.error('Full error:', result.error);
      return 'Transaction failed. Please try again.';
    }
    ```
  </Card>

  <Card title="Use Try-Catch for Network" icon="3">
    Wrap SDK calls in try-catch to handle network errors.

    ```typescript theme={null}
    try {
      const result = await paystack.transaction.list();
      // Handle result
    } catch (error) {
      // Handle network error
    }
    ```
  </Card>

  <Card title="Don't Swallow Errors" icon="4">
    Always handle or propagate errors. Never silently ignore them.

    ```typescript theme={null}
    // ❌ Bad
    if (result.error) {
      // Do nothing
    }

    // ✅ Good
    if (result.error) {
      throw new Error('Transaction failed');
    }
    ```
  </Card>
</CardGroup>

## Debugging Errors

<Tip>
  The SDK logs API error responses to the console automatically. Check `src/main/fetcher.ts:65-67`:

  ```typescript src/main/fetcher.ts theme={null}
  if (!response.ok) {
    console.error("API Error Response:", raw);
  }
  ```

  This helps you debug issues during development.
</Tip>

### Enable Detailed Logging

```typescript theme={null}
const result = await paystack.transaction.verify(reference);

if (result.error) {
  // Log everything for debugging
  console.log('Error name:', result.error.name);
  console.log('Error message:', result.error.message);
  console.log('Issues:', JSON.stringify(result.error.issues, null, 2));
  console.log('Formatted:', result.error.format());
  console.log('Flattened:', result.error.flatten());
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Type Safety" icon="shield-check" href="/core-concepts/type-safety">
    Learn about TypeScript types and runtime validation
  </Card>

  <Card title="Transactions" icon="money-bill" href="/api-reference/transaction">
    Explore the Transactions API
  </Card>
</CardGroup>
