# Error Handling

Guide to error handling patterns in the Deva SDK across components, authentication, and data fetching.

***

## Overview

The SDK handles errors at multiple levels:

1. **Authentication Errors** - OAuth flow failures, token issues
2. **Network Errors** - API request failures, timeouts
3. **Streaming Errors** - SSE connection issues, parsing errors
4. **Component Errors** - Channel not found, post creation failures
5. **Validation Errors** - Invalid input, missing required fields

***

## Authentication Errors

### OpenID Configuration Load Failure

Occurs when SDK cannot load OAuth configuration.

**Causes:**

* Network connectivity issues
* Invalid environment configuration
* Server unavailable

**Behavior:**

```typescript
// Provider.tsx
const { openIdConfig, error } = useOpenIdConfig(env);
if (error) {
  console.error("[sdk/provider] error fetching openid info: ", error);
}
```

**Impact:**

* Provider initialization fails
* Users cannot authenticate
* App cannot use SDK features

**Resolution:**

* Check network connectivity
* Verify environment setting
* Check Deva platform status

***

### Authorization Code Exchange Failure

Occurs during OAuth callback when exchanging code for tokens.

**Causes:**

* Invalid authorization code
* Code already used
* PKCE verification failure
* Network timeout

**Behavior:**

```typescript
const error_description = params.get("error_description")
  ?? "An unknown error occurred";
console.error(`[sdk/provider]: ${error_description}`);
setAuthError(error_description);
```

**Detection:**

```typescript
const { authError } = useDeva();

if (authError) {
  // Show error to user
  return <div>Authentication error: {authError}</div>;
}
```

**Resolution:**

* Retry login
* Clear browser storage
* Check redirect URI configuration

***

### Token Refresh Failure

Occurs when automatic token refresh fails.

**Causes:**

* Refresh token expired
* Network error
* Invalid refresh token
* Server error

**Behavior:**

```typescript
try {
  // Refresh token
} catch (error) {
  console.error("[sdk/provider]: Error refreshing token", error);
  // User logged out automatically
}
```

**Impact:**

* User automatically logged out
* Session cleared
* Must re-authenticate

**Prevention:**

* Don't manually clear sessionStorage
* Maintain active browser session
* Avoid long periods of inactivity

***

## Network Errors

### Data Fetching Errors

SWR automatically handles network errors with retries.

**Error Exposure:**

```typescript
const { data, error, isLoading } = useSWRFetcher<T>(url);

if (error) {
  // Handle error
  return <ErrorDisplay message={error.message} />;
}
```

**Retry Behavior:**

* Automatic retries with exponential backoff
* Continues using stale data if available
* Error state exposed via `error` property

**Example:**

```typescript
function ChannelData({ handle }: { handle: string }) {
  const { url, accessToken } = useDeva();
  const { data, error, isLoading } = useSWRFetcher<Channel>(
    `${url}/api/sdk/channel/handle/${handle}`
  );

  if (error) {
    return <div>Failed to load channel: {error.message}</div>;
  }

  if (isLoading) {
    return <div>Loading...</div>;
  }

  return <div>{data?.name}</div>;
}
```

***

### API Request Failures

Manual API calls require explicit error handling.

**Pattern:**

```typescript
try {
  const response = await fetcher<Post>(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${accessToken}`,
    },
    body: JSON.stringify(data),
  });
  return response;
} catch (error) {
  console.error("Error creating post:", error);
  throw error; // Re-throw or handle
}
```

**Component Usage:**

```typescript
const [error, setError] = useState<string | null>(null);

const handleSubmit = async () => {
  try {
    setError(null);
    await createPost(text);
  } catch (err) {
    setError("Failed to create post. Please try again.");
  }
};

return (
  <div>
    {error && <div className="error">{error}</div>}
    <button onClick={handleSubmit}>Submit</button>
  </div>
);
```

***

## Streaming Errors

### Stream Connection Failure

Occurs when SSE connection cannot be established.

**Causes:**

* Network connectivity issues
* Invalid message/thread ID
* Authentication failure
* Server error

**Detection:**

```typescript
const { message, isStreaming, error, isDone } = useStreamResponse({
  messageId,
  threadId
});

if (error) {
  return <MessageStreamError text={error.message} />;
}
```

**Error Component:**

```typescript
// MessageStreamError component
<div className="error-container">
  <div className="error-icon">🚧</div>
  <div className="error-content">
    <p className="error-title">Problem</p>
    <p className="error-message">{error.message}</p>
  </div>
</div>
```

**Behavior:**

```typescript
onopen: async (response) => {
  if (response.ok) {
    log("Stream connection opened");
  } else {
    throw new Error(
      `Failed to open stream: ${response.status} ${response.statusText}`
    );
  }
}
```

***

### Stream Parsing Errors

Occurs when stream event data cannot be parsed.

**Causes:**

* Malformed JSON
* Unexpected event format
* Encoding issues

**Handling:**

```typescript
onmessage: (msg) => {
  try {
    if (msg.event === "content") {
      const chunk = JSON.parse(msg.data);
      // Process chunk
    }
  } catch (err) {
    console.error("Error parsing stream data:", err);
    setError(
      err instanceof Error
        ? err
        : new Error("Error parsing stream data")
    );
  }
}
```

**Recovery:**

* Error displayed to user
* Stream aborted
* User can retry by sending new message

***

### Stream Interruption

Occurs when connection is lost during streaming.

**Causes:**

* Network disconnection
* Server timeout
* Component unmount
* User navigation

**Cleanup:**

```typescript
useEffect(() => {
  // Setup stream...

  return () => {
    // Cleanup on unmount
    if (controllerRef.current) {
      controllerRef.current.abort();
      controllerRef.current = null;
    }
    setIsStreaming(false);
  };
}, [messageId, threadId]);
```

**Graceful Handling:**

```typescript
onerror: (err: Error) => {
  console.error("Stream error:", err);
  setError(new Error(err.message || "Error streaming message"));
  setIsStreaming(false);
}
```

***

## Component-Specific Errors

### ChannelFeed Errors

**Channel Not Found:**

```typescript
const [channelError, setChannelError] = useState<string | null>(null);

useEffect(() => {
  if (channelFetchError) {
    setChannelError("Channel not found");
  } else {
    setChannelError(null);
  }
}, [channelFetchError]);

// Display
if (channelError) {
  return <div className="error">Channel not found</div>;
}
```

**Post Creation Failure:**

```typescript
const [error, setError] = useState<string | null>(null);

const handleCreatePost = async (text: string) => {
  try {
    setError(null);
    await createPost(text);
    setInputValue("");
  } catch (err) {
    setError("Failed to create post. Please try again.");
  }
};

// Display error in UI
{error && (
  <div className="error-banner">
    {error}
  </div>
)}
```

***

### Intercom Errors

**Deva Not Found:**

```typescript
const { data: personas } = useSWRFetcher<PaginatedPersonas>(
  `${url}/api/sdk/persona/public?usernames=${username}`
);

if (personas?.items.length === 0) {
  return <div>Deva not found</div>;
}
```

**Thread Creation Failure:**

```typescript
const { data: thread, error } = useSWRFetcher<Thread>(
  `${url}/api/sdk/chat/dm/${username}`
);

if (error) {
  return <div>Failed to load conversation: {error.message}</div>;
}
```

**Message Send Failure:**

* Handled internally by Intercom
* Error displayed to user
* Allows retry

***

## Error Boundaries

### React Error Boundary Pattern

Recommended for production apps:

```typescript
import { Component, ErrorInfo, ReactNode } from "react";

interface Props {
  children: ReactNode;
}

interface State {
  hasError: boolean;
  error?: Error;
}

class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error("DevaProvider error:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-fallback">
          <h2>Something went wrong</h2>
          <p>{this.state.error?.message}</p>
          <button onClick={() => window.location.reload()}>
            Reload
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <DevaProvider {...config}>
    <App />
  </DevaProvider>
</ErrorBoundary>
```

***

## Best Practices

### 1. Always Handle Errors

**Bad:**

```typescript
const { data } = useSWRFetcher<T>(url);
return <div>{data.value}</div>; // Crashes if data is undefined
```

**Good:**

```typescript
const { data, error, isLoading } = useSWRFetcher<T>(url);

if (error) return <ErrorDisplay error={error} />;
if (isLoading) return <Loading />;
if (!data) return null;

return <div>{data.value}</div>;
```

***

### 2. Provide User Feedback

**Bad:**

```typescript
try {
  await action();
} catch (err) {
  console.error(err); // User sees nothing
}
```

**Good:**

```typescript
const [error, setError] = useState<string | null>(null);

try {
  setError(null);
  await action();
} catch (err) {
  setError("Action failed. Please try again.");
}

return (
  <div>
    {error && <ErrorBanner message={error} />}
  </div>
);
```

***

### 3. Implement Retry Logic

```typescript
const [retryCount, setRetryCount] = useState(0);

const handleRetry = () => {
  setRetryCount(count => count + 1);
  setError(null);
};

return (
  <div>
    {error && (
      <div>
        <p>{error.message}</p>
        <button onClick={handleRetry}>Retry</button>
      </div>
    )}
  </div>
);
```

***

### 4. Log Errors Appropriately

```typescript
// Development: Log details
if (process.env.NODE_ENV === "development") {
  console.error("Detailed error:", error);
}

// Production: Log to monitoring service
if (process.env.NODE_ENV === "production") {
  errorMonitoringService.log(error);
}
```

***

### 5. Handle Edge Cases

```typescript
// Check authentication before actions
if (!isAuthenticated) {
  return <LoginPrompt />;
}

// Validate input
if (!text.trim()) {
  setError("Message cannot be empty");
  return;
}

// Check required data
if (!thread) {
  return <div>Loading conversation...</div>;
}
```

***

## Common Error Types

### Authentication Errors

| Error                      | Cause                 | Resolution                |
| -------------------------- | --------------------- | ------------------------- |
| OpenID config load failure | Network/server issue  | Check connectivity, retry |
| Code exchange failure      | Invalid auth code     | Retry login               |
| Token refresh failure      | Expired refresh token | Re-authenticate           |
| Invalid credentials        | Wrong client ID       | Check configuration       |

### Network Errors

| Error            | Cause                    | Resolution             |
| ---------------- | ------------------------ | ---------------------- |
| 401 Unauthorized | Invalid/expired token    | Re-authenticate        |
| 403 Forbidden    | Insufficient permissions | Check user access      |
| 404 Not Found    | Resource doesn't exist   | Verify ID/handle       |
| 500 Server Error | Server issue             | Retry, contact support |
| Network timeout  | Slow connection          | Retry with backoff     |

### Streaming Errors

| Error              | Cause                | Resolution        |
| ------------------ | -------------------- | ----------------- |
| Connection refused | Server unavailable   | Retry later       |
| Stream timeout     | Long response time   | Increase timeout  |
| Parse error        | Malformed data       | Report to support |
| Connection dropped | Network interruption | Retry message     |

***

## Error Response Format

API errors typically follow this format:

```typescript
{
  error: string;           // Error type
  message: string;         // Human-readable message
  status: number;          // HTTP status code
  details?: any;           // Additional context
}
```

***

## Related Documentation

* [DevaProvider](https://sdkdocs.deva.me/api-reference/deva-provider) - Provider error handling
* [Authentication Methods](https://sdkdocs.deva.me/api-reference/authentication) - Auth error handling
* [Streaming Methods](https://sdkdocs.deva.me/api-reference/authentication/streaming) - Stream error handling
* [Intercom Component](https://sdkdocs.deva.me/components/basic-usage-1) - Chat errors
* [ChannelFeed Component](https://sdkdocs.deva.me/components/basic-usage) - Feed errors
