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:
Authentication Errors - OAuth flow failures, token issues
Network Errors - API request failures, timeouts
Streaming Errors - SSE connection issues, parsing errors
Component Errors - Channel not found, post creation failures
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:
// 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:
const error_description = params.get("error_description")
?? "An unknown error occurred";
console.error(`[sdk/provider]: ${error_description}`);
setAuthError(error_description);Detection:
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:
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:
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
errorproperty
Example:
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:
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:
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:
const { message, isStreaming, error, isDone } = useStreamResponse({
messageId,
threadId
});
if (error) {
return <MessageStreamError text={error.message} />;
}Error Component:
// 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:
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:
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:
useEffect(() => {
// Setup stream...
return () => {
// Cleanup on unmount
if (controllerRef.current) {
controllerRef.current.abort();
controllerRef.current = null;
}
setIsStreaming(false);
};
}, [messageId, threadId]);Graceful Handling:
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:
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:
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:
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:
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:
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:
const { data } = useSWRFetcher<T>(url);
return <div>{data.value}</div>; // Crashes if data is undefinedGood:
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:
try {
await action();
} catch (err) {
console.error(err); // User sees nothing
}Good:
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
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
// 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
// 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
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
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
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:
{
error: string; // Error type
message: string; // Human-readable message
status: number; // HTTP status code
details?: any; // Additional context
}Related Documentation
DevaProvider - Provider error handling
Authentication Methods - Auth error handling
Streaming Methods - Stream error handling
Intercom Component - Chat errors
ChannelFeed Component - Feed errors
Last updated