fix(web): friend-add error toasts surface raw codes — read err.message, not err.body

The T19/T20 catch blocks looked for an `.body` property on thrown errors
to extract the structured error code. The shared API client (api/client.ts:298)
actually throws `new Error(body.error)` — the code lives in `err.message`,
and there's no `.body` attached.

Live E2E (T22 scenario 2) caught this: typing alice@orbit against an
awaiting_approval peer surfaced the raw code 'peer_pending_approval' as
the toast text instead of the human-readable mapServerErrorToMessage
output. Same defect would have hit every server-error toast on both
FriendsPage (AddFriend + UserDiscoverCard) and UserProfileModal.

Catch blocks now use err.message as both the code and the fallback text;
the inline comment points at the API client throw site so the contract
is documented at the consumer.
This commit is contained in:
Jannis Braun
2026-04-25 23:17:19 +02:00
parent 4a939b743f
commit fe969a7d96
2 changed files with 14 additions and 28 deletions
@@ -476,16 +476,12 @@ function AddFriendTab({
addToast('Friend request sent!', 'success'); addToast('Friend request sent!', 'success');
setQuery(''); setQuery('');
} catch (err) { } catch (err) {
const errorBody = (err as { body?: unknown })?.body; // The shared API client throws `new Error(body.error)` for non-2xx
let code: string | undefined; // responses (api/client.ts:298), so `err.message` carries the server's
let message: string | undefined; // error code (e.g. 'peer_pending_approval'). It also doubles as fallback
if (errorBody && typeof errorBody === 'object') { // text if the code is unrecognized by mapServerErrorToMessage.
code = (errorBody as { error?: string }).error; const code = err instanceof Error ? err.message : undefined;
message = (errorBody as { message?: string }).message; addToast(mapServerErrorToMessage(code, code, query.trim()), 'warning');
}
// Fall back to the Error message if the server didn't send a structured body.
const fallback = message ?? (err instanceof Error ? err.message : undefined);
addToast(mapServerErrorToMessage(code, fallback, query.trim()), 'warning');
} finally { } finally {
setDirectAddLoading(false); setDirectAddLoading(false);
} }
@@ -643,15 +639,9 @@ function UserDiscoverCard({
const requestId = await sendFriendRequest(username); const requestId = await sendFriendRequest(username);
onRelationshipChange(user.id, user._instanceOrigin, 'outbound_pending', requestId); onRelationshipChange(user.id, user._instanceOrigin, 'outbound_pending', requestId);
} catch (err) { } catch (err) {
const errorBody = (err as { body?: unknown })?.body; // See handleDirectAdd above — err.message is the server error code.
let code: string | undefined; const code = err instanceof Error ? err.message : undefined;
let message: string | undefined; setError(mapServerErrorToMessage(code, code ?? 'Failed to send request', username));
if (errorBody && typeof errorBody === 'object') {
code = (errorBody as { error?: string }).error;
message = (errorBody as { message?: string }).message;
}
const fallback = message ?? (err instanceof Error ? err.message : 'Failed to send request');
setError(mapServerErrorToMessage(code, fallback, username));
} finally { } finally {
setActionLoading(false); setActionLoading(false);
} }
@@ -186,15 +186,11 @@ export function UserProfileModal() {
try { try {
await sendFriendRequest(user.username); await sendFriendRequest(user.username);
} catch (err) { } catch (err) {
const errorBody = (err as { body?: unknown })?.body; // The shared API client throws `new Error(body.error)` for non-2xx
let code: string | undefined; // responses (api/client.ts:298), so err.message carries the server's
let message: string | undefined; // error code (e.g. 'peer_pending_approval').
if (errorBody && typeof errorBody === 'object') { const code = err instanceof Error ? err.message : undefined;
code = (errorBody as { error?: string }).error; addToast(mapServerErrorToMessage(code, code, user.username), 'warning');
message = (errorBody as { message?: string }).message;
}
const fallback = message ?? (err instanceof Error ? err.message : undefined);
addToast(mapServerErrorToMessage(code, fallback, user.username), 'warning');
} finally { } finally {
setFriendActionLoading(false); setFriendActionLoading(false);
} }