fix(ux): human-readable API errors instead of raw api_error_NNN codes
All checks were successful
Deploy to Production / deploy (push) Successful in 1m20s

apiFetch threw new Error(api_error_<status>), so any UI fallback to (e).message showed a cryptic code, and some flows surfaced bare codes like slug_taken. Added a central humanizeError() in lib/api.ts: prefers the backend detail sentence, then a mapped friendly message per known code, then a status-based fallback - never a raw code. apiFetch now sets the thrown error message via it, so every (e).message fallback across the app becomes a real sentence. Wizard analyze/build now use humanizeError directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marco Sadjadi
2026-05-31 21:08:39 +02:00
parent 2a12ea18cd
commit 3dc65e4f4d
2 changed files with 64 additions and 7 deletions

View File

@@ -5,7 +5,7 @@ import { Input, Label, Textarea } from '@/components/input';
import { InstallSnippets } from '@/components/install-snippets';
import { StreamingLogs } from '@/components/streaming-logs';
import { Button } from '@/components/ui/button';
import { apiFetch, apiSseStream } from '@/lib/api';
import { apiFetch, apiSseStream, humanizeError } from '@/lib/api';
import { findSecretInPrompt } from '@bmm/types';
import { Loader2, RotateCcw, X } from 'lucide-react';
import Link from 'next/link';
@@ -302,8 +302,7 @@ function NewServerPageInner() {
setStep('confirm');
return;
} catch (e) {
const detail = (e as { detail?: { error?: string; detail?: string } }).detail;
setError(detail?.detail ?? detail?.error ?? (e as Error).message);
setError(humanizeError(e));
setStep('prompt');
return;
}
@@ -458,7 +457,7 @@ function NewServerPageInner() {
setError(detail?.detail ?? 'Daily build limit reached — try again tomorrow or upgrade.');
return;
}
setError(detail?.detail ?? code ?? (e as Error).message);
setError(humanizeError(e));
}
}