feat: unify error handling + handle case of malformed config.yaml (#2058)

This commit is contained in:
Alex Hancock
2025-04-07 12:54:18 -04:00
committed by GitHub
parent a4e7d4ef5c
commit 926a511e95
5 changed files with 79 additions and 58 deletions
+21 -3
View File
@@ -55,6 +55,14 @@ interface ConfigProviderProps {
children: React.ReactNode;
}
export class MalformedConfigError extends Error {
constructor() {
super('Check contents of ~/.config/goose/config.yaml');
this.name = 'MalformedConfigError';
Object.setPrototypeOf(this, MalformedConfigError.prototype);
}
}
const ConfigContext = createContext<ConfigContextType | undefined>(undefined);
export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
@@ -160,10 +168,20 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
};
const getExtensions = async (forceRefresh = false): Promise<FixedExtensionEntry[]> => {
// If a refresh is forced, or we don't have providers yet
if (forceRefresh || extensionsList.length === 0) {
// If a refresh is forced, or we don't have providers yet
const response = await apiGetExtensions();
const extensionResponse: ExtensionResponse = response.data;
const result = await apiGetExtensions();
if (result.response.status === 422) {
throw new MalformedConfigError();
}
if (result.error && !result.data) {
console.log(result.error);
return;
}
const extensionResponse: ExtensionResponse = result.data;
setExtensionsList(extensionResponse.extensions);
return extensionResponse.extensions;
}
+38 -10
View File
@@ -1,4 +1,6 @@
import React from 'react';
import { Button } from './ui/button';
import { AlertTriangle } from 'lucide-react';
// Capture unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
@@ -12,17 +14,48 @@ window.addEventListener('error', (event) => {
);
});
export function ErrorUI({ error }) {
return (
<div className="fixed inset-0 w-full h-full flex flex-col items-center justify-center gap-6 bg-background">
<div className="flex flex-col items-center gap-4 max-w-[600px] text-center px-6">
<div className="w-16 h-16 rounded-full bg-destructive/10 flex items-center justify-center mb-2">
<AlertTriangle className="w-8 h-8 text-destructive" />
</div>
<h1 className="text-2xl font-semibold text-foreground">
Honk!
</h1>
<p className="text-base text-textSubtle mb-2">
An error occurred.
</p>
<pre className="text-destructive text-sm p-4 bg-muted rounded-lg w-full overflow-auto border border-border">
{error.message}
</pre>
<Button
className="flex items-center gap-2 flex-1 justify-center text-white dark:text-textSubtle bg-black dark:bg-white hover:bg-subtle"
onClick={() => window.electron.reloadApp()}
>
Reload
</Button>
</div>
</div>
);
}
export class ErrorBoundary extends React.Component<
{ children: React.ReactNode },
{ hasError: boolean }
{ error: Error, hasError: boolean }
> {
constructor(props: { children: React.ReactNode }) {
super(props);
this.state = { hasError: false };
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(_: Error) {
return { hasError: true };
static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
@@ -32,13 +65,8 @@ export class ErrorBoundary extends React.Component<
render() {
if (this.state.hasError) {
return (
<div className="fixed inset-0 w-full h-full flex items-center justify-center bg-background">
<h1 className="text-xl font-semibold text-foreground">Something went wrong.</h1>
</div>
);
return <ErrorUI error={this.state.error} />
}
return this.props.children;
}
}
-31
View File
@@ -1,31 +0,0 @@
import React from 'react';
import { Card } from './ui/card';
interface ErrorScreenProps {
error: string;
onReload: () => void;
}
const ErrorScreen: React.FC<ErrorScreenProps> = ({ error, onReload }) => {
return (
<div className="chat-content flex flex-col w-screen h-screen bg-window-gradient dark:bg-dark-window-gradient items-center justify-center p-[10px]">
<div className="titlebar-drag-region" />
<div className="relative block h-[20px] w-screen" />
<Card className="flex flex-col flex-1 h-[calc(100vh-95px)] w-full bg-card-gradient dark:bg-dark-card-gradient mt-0 border-none shadow-xl rounded-2xl relative">
<div className="flex flex-col items-center justify-center h-full p-4">
<div className="text-red-700 dark:text-red-300 bg-red-400/50 p-3 rounded-lg mb-2">
{'Honk! Goose experienced a fatal error'}
</div>
<div
className="p-4 text-center text-splash-pills-text whitespace-nowrap cursor-pointer bg-prev-goose-gradient dark:bg-dark-prev-goose-gradient text-prev-goose-text dark:text-prev-goose-text-dark rounded-[14px] inline-block hover:scale-[1.02] transition-all duration-150"
onClick={onReload}
>
Reload
</div>
</div>
</Card>
</div>
);
};
export default ErrorScreen;