Error Handling
Patterns for handling errors on both the agent side (MCP Client) and the frontend (React SDK).
Agent-Side Error Handling
Section titled “Agent-Side Error Handling”Retry with exponential backoff
Section titled “Retry with exponential backoff”import { GguiClient, GguiTimeoutError, GguiAuthError, GguiSessionNotFoundError, McpProtocolError,} from "@ggui-ai/mcp-client";
const ggui = new GguiClient({ apiKey: process.env.GGUI_API_KEY!, appId: process.env.GGUI_APP_ID!, timeout: 60000,});
async function pushWithRetry( intent: string, prompt: string, maxRetries = 3): Promise<{ sessionId: string; url: string }> { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await ggui.push({ story: { intent, prompt } }); } catch (error) { if (error instanceof GguiAuthError) { throw new Error("Invalid API key. Check your GGUI_API_KEY."); } if (attempt === maxRetries) throw error; if (error instanceof GguiTimeoutError || error instanceof McpProtocolError) { const delay = Math.min(1000 * Math.pow(2, attempt), 10000); console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms...`); await new Promise((r) => setTimeout(r, delay)); continue; } throw error; } } throw new Error("Unreachable");}Graceful session recovery
Section titled “Graceful session recovery”async function resilientWait(sessionId: string) { try { return await ggui.waitForCompletion(sessionId, { maxWait: 300_000 }); } catch (error) { if (error instanceof GguiSessionNotFoundError) { console.log("Session expired, creating new one..."); const { sessionId: newId } = await ggui.push({ story: { intent: "Recover from expired session", prompt: "The previous session expired. Please try again.", }, }); return ggui.waitForCompletion(newId); } throw error; }}Classifying errors for user messaging
Section titled “Classifying errors for user messaging”function getUserMessage(error: unknown): string { if (error instanceof GguiAuthError) return "Authentication failed. Please check your API configuration."; if (error instanceof GguiTimeoutError) return "The request timed out. Please try again."; if (error instanceof GguiSessionNotFoundError) return "Your session has expired."; if (error instanceof McpProtocolError && error.code === -32004) return "Failed to generate the UI. Try a simpler request."; return "An unexpected error occurred.";}Frontend Error Handling
Section titled “Frontend Error Handling”Component-level error handling
Section titled “Component-level error handling”import { StackItemRenderer } from "@ggui-ai/react";
function SafeRenderer({ stackItem }) { const [renderError, setRenderError] = useState<Error | null>(null);
if (renderError) { return ( <div> <p>This component failed to render.</p> <button onClick={() => setRenderError(null)}>Retry</button> </div> ); }
return ( <StackItemRenderer stackItem={stackItem} onError={(error) => setRenderError(error)} fallback={<div>Loading...</div>} /> );}Self-repair for automatic recovery
Section titled “Self-repair for automatic recovery”import { SelfRepairBoundary, DynamicComponent } from "@ggui-ai/react";
<SelfRepairBoundary sessionId={sessionId} appId={appId} stackItemId={stackItem.id} config={{ enabled: true, maxAttempts: 3, retryDelayMs: 2000, showRepairUI: true }} onReportError={async (report) => { await fetch("/api/report-error", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(report), }); }} repairingFallback={<div>Auto-fixing component...</div>} errorFallback={<div>This component could not be repaired.</div>}> <DynamicComponent code={stackItem.componentCode} /></SelfRepairBoundary>;Connection resilience
Section titled “Connection resilience”{ ({ submit, connectionStatus }) => ( <form onSubmit={(e) => { e.preventDefault(); submit(formData); // Events are buffered by WebSocketManager when disconnected }} > <button type="submit">Submit</button> {connectionStatus !== "connected" && <small>Connection: {connectionStatus}</small>} </form> );}