Skip to content

Commit c2a83c6

Browse files
kuco23claude
andcommitted
claude:feat: make ServerError show the actual status and message
ServerError previously rendered "500 / Server error / <stringified-error>" regardless of what failed. Derive the status from ApiError.status, map it to a meaningful label (404 Not found, 503 Service unavailable, etc.), and pull the description from the response body's error/message fields with a truncate guard. Network/unknown errors fall back to "Connection failed". Call sites switch from <ServerError status={500} message={error} /> to <ServerError error={error} />. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 567369b commit c2a83c6

9 files changed

Lines changed: 84 additions & 14 deletions

File tree

src/components/sections/callToAction.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const CallToAction = () => {
3939
</div>
4040
} else if (data == null) {
4141
hasError = true
42-
component = <ServerError status={500} message={error} />
42+
component = <ServerError error={error} />
4343
} else {
4444
const proposal = getProposalData(data)
4545
if (proposal.length > 0) {

src/components/ui/serverError.tsx

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,79 @@
1-
export const ServerError = ({ status, message }) => {
2-
return <div className="error-container">
3-
<div className="error-status">{status}</div>
4-
<div className="error-label">Server error</div>
5-
<p className="error-desc">{String(message ?? 'The request failed.')}</p>
6-
</div>
1+
import { ApiError } from '~/backendApi'
2+
3+
type ServerErrorProps = {
4+
error?: unknown
5+
status?: number
6+
className?: string
7+
}
8+
9+
const MAX_DESC_CHARS = 280
10+
11+
const labelForStatus = (status: number): string => {
12+
if (status === 0) return 'Connection failed'
13+
if (status === 400) return 'Bad request'
14+
if (status === 401) return 'Unauthorized'
15+
if (status === 403) return 'Forbidden'
16+
if (status === 404) return 'Not found'
17+
if (status === 408) return 'Request timeout'
18+
if (status === 409) return 'Conflict'
19+
if (status === 429) return 'Too many requests'
20+
if (status === 502) return 'Bad gateway'
21+
if (status === 503) return 'Service unavailable'
22+
if (status === 504) return 'Gateway timeout'
23+
if (status >= 500) return 'Server error'
24+
if (status >= 400) return 'Request failed'
25+
return 'Error'
26+
}
27+
28+
const extractMessage = (error: unknown): string | undefined => {
29+
if (error == null) return undefined
30+
if (typeof error === 'string') return error.trim() || undefined
31+
if (error instanceof ApiError) {
32+
const body = error.body as unknown
33+
if (body && typeof body === 'object') {
34+
const b = body as Record<string, unknown>
35+
if (typeof b.error === 'string' && b.error) return b.error
36+
if (typeof b.message === 'string' && b.message) return b.message
37+
}
38+
if (typeof body === 'string' && body) return body
39+
if (error.message) return error.message
40+
return error.statusText || undefined
41+
}
42+
if (error instanceof Error) return error.message || undefined
43+
try { return String(error) } catch { return undefined }
44+
}
45+
46+
const truncate = (text: string): string =>
47+
text.length <= MAX_DESC_CHARS ? text : text.slice(0, MAX_DESC_CHARS - 1).trimEnd() + '…'
48+
49+
const derive = (error: unknown, statusOverride?: number) => {
50+
let status = statusOverride
51+
if (status == null && error instanceof ApiError) status = error.status
52+
53+
const message = extractMessage(error)
54+
const hasStatus = status != null && status > 0
55+
56+
return {
57+
statusDisplay: hasStatus ? String(status) : '—',
58+
label: hasStatus ? labelForStatus(status!) : 'Connection failed',
59+
description: message
60+
? truncate(message)
61+
: hasStatus
62+
? 'The request failed.'
63+
: 'Could not reach the server. Check your connection and try again.',
64+
}
65+
}
66+
67+
export const ServerError = ({ error, status, className }: ServerErrorProps) => {
68+
const { statusDisplay, label, description } = derive(error, status)
69+
const containerClass = className ? `error-container ${className}` : 'error-container'
70+
return (
71+
<div className={containerClass}>
72+
<div className="error-status">{statusDisplay}</div>
73+
<div className="error-label">{label}</div>
74+
<p className="error-desc">{description}</p>
75+
</div>
76+
)
777
}
878

979
export default ServerError

src/pages/protocols/avalanche-validator/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const AvalancheValidatorPage = () => {
2222
</div>
2323
</>
2424
} else if (error != null) {
25-
component = <ServerError status={500} message={error} />
25+
component = <ServerError error={error} />
2626
} else {
2727
component = <>
2828
<InfoComponent specs={data.specs} summary={data.summary} />

src/pages/protocols/flare-fsp/components/delegateLocal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const FlareFspLocalDelegateComponent = () => {
5555
<SpinnerCircular color={C.FLARE_COLOR_CODE} size={45} />
5656
</div>
5757
} else if (data == null) {
58-
component = <ServerError status={500} message={error} />
58+
component = <ServerError error={error} />
5959
} else {
6060
component = <>
6161
<p>

src/pages/protocols/flare-fsp/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const FlareFspPage = () => {
2727
</div>
2828
</>
2929
} else if (data == null) {
30-
component = <ServerError status={500} message={error} />
30+
component = <ServerError error={error} />
3131
} else {
3232
const delegator = <HashLink url={flareEvmAddressUrl(data.info.delegationAddress)} address={data.info.delegationAddress} />
3333
component = <>

src/pages/protocols/flare-validator/components/delegateLocal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ const FlareValidatorLocalDelegateComponent = () => {
121121
</div>
122122
</div>
123123
} else if (resp?.data == null) {
124-
component = <ServerError status={500} message={error} />
124+
component = <ServerError error={error} />
125125
} else {
126126
const delegated = resp.data.delegations.reduce((x, y) => x + y.delegated, 0)
127127
component = <div>

src/pages/protocols/flare-validator/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const FlareValidatorPage = () => {
2424
</div>
2525
</>
2626
} else if (error != null) {
27-
component = <ServerError status={500} message={error} />
27+
component = <ServerError error={error} />
2828
} else {
2929
component = <>
3030
<InfoComponent specs={data.specs} summary={data.summary} />

src/pages/protocols/songbird-fsp/components/delegateLocal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const SongbirdFspLocalDelegateComponent = () => {
5151
<SpinnerCircular color={C.SONGBIRD_COLOR_CODE} size={45} />
5252
</div>
5353
} else if (data == null) {
54-
component = <ServerError status={500} message={error} />
54+
component = <ServerError error={error} />
5555
} else {
5656
component = <>
5757
<p>

src/pages/protocols/songbird-fsp/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const SongbirdFspPage = () => {
2626
</div>
2727
</>
2828
} else if (data == null) {
29-
component = <ServerError status={500} message={error} />
29+
component = <ServerError error={error} />
3030
} else {
3131
const link = <HashLink url={songbirdEvmAddressUrl(data.info.delegationAddress)} address={data.info.delegationAddress} />
3232
component = <>

0 commit comments

Comments
 (0)