diff --git a/lib/object-storage-web/src/use-upload.ts b/lib/object-storage-web/src/use-upload.ts index 7ec850e4..7808fa66 100644 --- a/lib/object-storage-web/src/use-upload.ts +++ b/lib/object-storage-web/src/use-upload.ts @@ -75,7 +75,14 @@ export function useUpload(options: UseUploadOptions = {}) { if (!response.ok) { const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.error || "Failed to get upload URL"); + const serverMsg = + (typeof errorData?.error === "string" && errorData.error) || + (typeof errorData?.message === "string" && errorData.message) || + ""; + const detail = serverMsg + ? `${serverMsg} (HTTP ${response.status})` + : `HTTP ${response.status} ${response.statusText || ""}`.trim(); + throw new Error(`Failed to get upload URL: ${detail}`); } return response.json(); @@ -85,16 +92,39 @@ export function useUpload(options: UseUploadOptions = {}) { const uploadToPresignedUrl = useCallback( async (file: File, uploadURL: string): Promise => { - const response = await fetch(uploadURL, { - method: "PUT", - body: file, - headers: { - "Content-Type": file.type || "application/octet-stream", - }, - }); + let response: Response; + try { + response = await fetch(uploadURL, { + method: "PUT", + body: file, + headers: { + "Content-Type": file.type || "application/octet-stream", + }, + }); + } catch (err) { + // Network-level failure (CORS, DNS, unreachable host). This is + // the typical symptom when PUBLIC_BASE_URL on the server points + // at an origin the browser can't actually reach (e.g. localhost + // while the user is on Tailscale). + const msg = err instanceof Error ? err.message : String(err); + let host = ""; + try { + host = new URL(uploadURL).host; + } catch { + /* ignore */ + } + throw new Error( + `Failed to upload file to storage: network error reaching ${host || "the storage endpoint"} — ${msg}`, + ); + } if (!response.ok) { - throw new Error("Failed to upload file to storage"); + const bodyText = await response.text().catch(() => ""); + const snippet = bodyText.slice(0, 200).trim(); + const detail = snippet + ? `${snippet} (HTTP ${response.status})` + : `HTTP ${response.status} ${response.statusText || ""}`.trim(); + throw new Error(`Failed to upload file to storage: ${detail}`); } }, []