fix: error scenario when checking provider (#8394)

This commit is contained in:
Lifei Zhou
2026-04-09 01:07:06 +10:00
committed by GitHub
parent adf9e1fa79
commit 4506d00eff
3 changed files with 68 additions and 15 deletions
+5 -2
View File
@@ -33,7 +33,7 @@ interface ConfigContextType {
extensionsList: FixedExtensionEntry[];
extensionWarnings: string[];
upsert: (key: string, value: unknown, is_secret: boolean) => Promise<void>;
read: (key: string, is_secret: boolean) => Promise<unknown>;
read: (key: string, is_secret: boolean, options?: { throwOnError?: boolean }) => Promise<unknown>;
remove: (key: string, is_secret: boolean) => Promise<void>;
addExtension: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>;
toggleExtension: (name: string) => Promise<void>;
@@ -88,11 +88,14 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
[reloadConfig]
);
const read = useCallback(async (key: string, is_secret: boolean = false) => {
const read = useCallback(async (key: string, is_secret: boolean = false, options?: { throwOnError?: boolean }) => {
const query: ConfigKeyQuery = { key: key, is_secret: is_secret };
const response = await readConfig({
body: query,
});
if (options?.throwOnError && response.error) {
throw response.error;
}
return response.data;
}, []);
@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { useConfig } from '../ConfigContext';
import { useModelAndProvider } from '../ModelAndProviderContext';
import { Goose } from '../icons';
import { Button } from '../ui/button';
import ProviderSelector from './ProviderSelector';
import OnboardingSuccess from './OnboardingSuccess';
import {
@@ -23,6 +24,18 @@ const i18n = defineMessages({
id: 'onboardingGuard.welcomeDescription',
defaultMessage: 'Your local AI agent. Connect an AI model provider to get started.',
},
checkProviderErrorTitle: {
id: 'onboardingGuard.checkProviderErrorTitle',
defaultMessage: 'Unable to connect to Goose server',
},
checkProviderErrorDescription: {
id: 'onboardingGuard.checkProviderErrorDescription',
defaultMessage: 'The server may be starting up or temporarily unavailable.',
},
retry: {
id: 'onboardingGuard.retry',
defaultMessage: 'Retry',
},
});
const TELEMETRY_CONFIG_KEY = 'GOOSE_TELEMETRY_ENABLED';
@@ -39,6 +52,7 @@ export default function OnboardingGuard({ children }: OnboardingGuardProps) {
const [isCheckingProvider, setIsCheckingProvider] = useState(true);
const [hasProvider, setHasProvider] = useState(false);
const [checkProviderError, setCheckProviderError] = useState(false);
const [hasSelection, setHasSelection] = useState(false);
const [configuredProvider, setConfiguredProvider] = useState<string | null>(null);
const [configuredProviderDisplayName, setConfiguredProviderDisplayName] = useState<string | null>(
@@ -47,27 +61,37 @@ export default function OnboardingGuard({ children }: OnboardingGuardProps) {
const [configuredModel, setConfiguredModel] = useState<string | null>(null);
const hasTrackedOnboardingStart = useRef(false);
useEffect(() => {
const checkProvider = async () => {
const checkProvider = async (retries = 3, delay = 1000) => {
setIsCheckingProvider(true);
setCheckProviderError(false);
for (let attempt = 0; attempt <= retries; attempt++) {
try {
const provider = ((await read('GOOSE_PROVIDER', false)) as string) || '';
setHasProvider(provider.trim() !== '');
} catch (error) {
console.error('Error checking provider:', error);
setHasProvider(false);
} finally {
const provider = (await read('GOOSE_PROVIDER', false, { throwOnError: true })) as string | null;
setHasProvider(!!provider?.trim());
setIsCheckingProvider(false);
return;
} catch (error) {
console.error(`Error checking provider (attempt ${attempt + 1}/${retries + 1}):`, error);
if (attempt < retries) {
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
};
checkProvider();
}, [read]);
}
setCheckProviderError(true);
setIsCheckingProvider(false);
};
useEffect(() => {
if (!isCheckingProvider && !hasProvider && !hasTrackedOnboardingStart.current) {
checkProvider();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (!isCheckingProvider && !hasProvider && !checkProviderError && !hasTrackedOnboardingStart.current) {
trackOnboardingStarted();
hasTrackedOnboardingStart.current = true;
}
}, [isCheckingProvider, hasProvider]);
}, [isCheckingProvider, hasProvider, checkProviderError]);
const handleConfigured = async (providerName: string, modelId?: string) => {
trackOnboardingProviderSelected({ provider: providerName });
@@ -107,6 +131,23 @@ export default function OnboardingGuard({ children }: OnboardingGuardProps) {
return null;
}
if (checkProviderError) {
return (
<div className="h-screen w-full bg-background-default flex flex-col items-center justify-center">
<div className="text-center max-w-md">
<div className="mb-4">
<Goose className="size-8 mx-auto" />
</div>
<h1 className="text-xl font-light mb-3">{intl.formatMessage(i18n.checkProviderErrorTitle)}</h1>
<p className="text-text-muted mb-6">{intl.formatMessage(i18n.checkProviderErrorDescription)}</p>
<Button onClick={() => checkProvider()}>
{intl.formatMessage(i18n.retry)}
</Button>
</div>
</div>
);
}
if (hasProvider) {
return <>{children}</>;
}
+9
View File
@@ -2387,6 +2387,15 @@
"navigationStyleSelector.tileLabel": {
"defaultMessage": "Tile"
},
"onboardingGuard.checkProviderErrorDescription": {
"defaultMessage": "The server may be starting up or temporarily unavailable."
},
"onboardingGuard.checkProviderErrorTitle": {
"defaultMessage": "Unable to connect to Goose server"
},
"onboardingGuard.retry": {
"defaultMessage": "Retry"
},
"onboardingGuard.welcomeDescription": {
"defaultMessage": "Your local AI agent. Connect an AI model provider to get started."
},