# 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](/api-reference/deva-provider.md) - Provider error handling
* [Authentication Methods](/api-reference/authentication.md) - Auth error handling
* [Streaming Methods](/api-reference/authentication/streaming.md) - Stream error handling
* [Intercom Component](/components/basic-usage-1.md) - Chat errors
* [ChannelFeed Component](/components/basic-usage.md) - Feed errors


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://sdkdocs.deva.me/api-reference/error-handling.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
