fix: surface backend error details instead of raw axios strings

This commit is contained in:
Ashen
2026-08-10 18:28:18 +05:30
parent 2e04cf5925
commit ee18930605
5 changed files with 42 additions and 18 deletions
+6 -7
View File
@@ -3,6 +3,7 @@
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { getApiErrorMessage } from '@/lib/api/errors';
import {
AdminService,
ProviderModels,
@@ -139,7 +140,7 @@ export default function ProvidersPage() {
resetForm();
},
onError: (error: Error) => {
toast.error(`Failed to create provider: ${error.message}`);
toast.error(getApiErrorMessage(error, 'Failed to create provider'));
},
});
@@ -153,7 +154,7 @@ export default function ProvidersPage() {
toast.success('Provider updated successfully');
},
onError: (error: Error) => {
toast.error(`Failed to update provider: ${error.message}`);
toast.error(getApiErrorMessage(error, 'Failed to update provider'));
},
});
@@ -164,7 +165,7 @@ export default function ProvidersPage() {
toast.success('Provider deleted successfully');
},
onError: (error: Error) => {
toast.error(`Failed to delete provider: ${error.message}`);
toast.error(getApiErrorMessage(error, 'Failed to delete provider'));
},
});
@@ -183,7 +184,7 @@ export default function ProvidersPage() {
toast.success('Model deleted successfully');
},
onError: (error: Error) => {
toast.error(`Failed to delete model: ${error.message}`);
toast.error(getApiErrorMessage(error, 'Failed to delete model'));
},
});
@@ -205,9 +206,7 @@ export default function ProvidersPage() {
toast.success('Account created, but no API key returned.');
}
} catch (error: unknown) {
const errorMessage =
error instanceof Error ? error.message : 'Unknown error';
toast.error(`Failed to create account: ${errorMessage}`);
toast.error(getApiErrorMessage(error, 'Failed to create account'));
} finally {
setIsCreatingAccount(false);
}
+3 -1
View File
@@ -4,6 +4,7 @@ import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { RefreshCw, AlertCircle, Wallet, User, Coins } from 'lucide-react';
import { WalletService, BalanceDetail } from '@/lib/api/services/wallet';
import { getApiErrorMessage } from '@/lib/api/errors';
import {
Card,
CardContent,
@@ -191,7 +192,8 @@ export function DetailedWalletBalance({
<Alert variant='destructive'>
<AlertCircle className='h-5 w-5' />
<AlertDescription>
Error loading balance: {(error as Error).message}
Error loading balance:{' '}
{getApiErrorMessage(error, 'Request failed')}
</AlertDescription>
</Alert>
) : (
@@ -21,6 +21,7 @@ import { Skeleton } from '@/components/ui/skeleton';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { AlertCircle, Copy, Trash2, Check } from 'lucide-react';
import { toast } from 'sonner';
import { getApiErrorMessage } from '@/lib/api/errors';
function formatTs(ts: number | null): string {
if (!ts) return '—';
@@ -44,9 +45,7 @@ export function CliTokensSettings(): React.ReactElement {
const data = await AdminService.listCliTokens();
setTokens(data);
} catch (err: unknown) {
const message =
err instanceof Error ? err.message : 'Failed to load tokens';
setError(message);
setError(getApiErrorMessage(err, 'Failed to load tokens'));
} finally {
setLoading(false);
}
@@ -79,9 +78,7 @@ export function CliTokensSettings(): React.ReactElement {
await loadTokens();
toast.success('Token created. Copy it now — it will not be shown again.');
} catch (err: unknown) {
const message =
err instanceof Error ? err.message : 'Failed to create token';
toast.error(message);
toast.error(getApiErrorMessage(err, 'Failed to create token'));
} finally {
setCreating(false);
}
@@ -98,9 +95,7 @@ export function CliTokensSettings(): React.ReactElement {
await loadTokens();
toast.success('Token revoked');
} catch (err: unknown) {
const message =
err instanceof Error ? err.message : 'Failed to revoke token';
toast.error(message);
toast.error(getApiErrorMessage(err, 'Failed to revoke token'));
}
}
+3 -1
View File
@@ -13,6 +13,7 @@ import {
ChevronRight,
} from 'lucide-react';
import { AdminService } from '@/lib/api/services/admin';
import { getApiErrorMessage } from '@/lib/api/errors';
import {
Card,
CardContent,
@@ -169,7 +170,8 @@ export function TemporaryBalances({
<Alert variant='destructive'>
<AlertCircle className='h-5 w-5' />
<AlertDescription>
Error loading API keys: {(error as Error).message}
Error loading API keys:{' '}
{getApiErrorMessage(error, 'Request failed')}
</AlertDescription>
</Alert>
) : (
+26
View File
@@ -0,0 +1,26 @@
import { isAxiosError } from 'axios';
/**
* Human-readable message for a failed API call. Prefers the backend's
* `detail` field; never surfaces raw axios strings like
* "Request failed with status code 500".
*/
export function getApiErrorMessage(
error: unknown,
fallback = 'Something went wrong'
): string {
if (isAxiosError(error)) {
if (!error.response) {
return 'Cannot reach the node. Is it running?';
}
const detail: unknown = error.response.data?.detail;
if (typeof detail === 'string' && detail) {
return detail;
}
return `${fallback} (HTTP ${error.response.status})`;
}
if (error instanceof Error && error.message) {
return error.message;
}
return fallback;
}