Stirling-Tools/Stirling-PDF · error · Error
Download failed (${response.status})
Error message
Download failed (${response.status}) What it means
Thrown by downloadFromUrl() when fetch(url) resolves with response.ok === false. The HTTP status code is interpolated into the message. The blob is never read; this guards upstream errors (auth required, not found, server error) before attempting to save.
Source
Thrown at frontend/editor/src/desktop/services/downloadService.ts:43
return { cancelled: true };
}
const result = await saveToLocalPath(request.data, savePath);
if (!result.success) {
throw new Error(result.error || "Failed to save file");
}
return { savedPath: savePath };
}
export async function downloadFromUrl(
url: string,
filename: string,
localPath?: string,
): Promise<DownloadResult> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Download failed (${response.status})`);
}
const blob = await response.blob();
return downloadFile({ data: blob, filename, localPath });
}
View on GitHub (pinned to 9ef20dcab8)
Solutions
- Parse the status from the message (or refactor to expose it) and handle 401/403 by refreshing the token, 404 by re-fetching a fresh link, 5xx by retrying.
- Ensure the request carries the needed Authorization header / cookies for protected resources.
- For signed URLs, generate a fresh one before retrying.
- Log the URL (without secrets) + status to identify the failing resource.
Example fix
// before
await downloadFromUrl(url, name);
// after: retry on transient 5xx, refresh auth on 401
try { await downloadFromUrl(url, name); }
catch (e) {
const status = Number(/\((\d+)\)/.exec(e.message)?.[1]);
if (status === 401) { await authService.refresh(); await downloadFromUrl(url, name); return; }
if (status >= 500) { /* retry with backoff */ return; }
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// sanity-check the URL before fetching
function isDownloadableUrl(u: string): boolean { try { const p = new URL(u); return p.protocol === 'http:' || p.protocol === 'https:'; } catch { return false; } } Type guard
function isDownloadStatusError(e: unknown): e is Error {
return e instanceof Error && /^Download failed \(\d+\)$/.test(e.message);
} Try / catch
try { await downloadFromUrl(url, name); }
catch (e) {
if (isDownloadStatusError(e)) {
const status = Number(/\((\d+)\)/.exec(e.message)?.[1]);
if (status === 401) { await authService.refresh(); return downloadFromUrl(url, name); }
if (status >= 500) { /* retry with backoff */ return; }
}
throw e;
} Prevention
- Attach the auth token to protected download URLs.
- Refresh signed URLs before they expire.
- Parse the status to drive retry vs re-auth vs give-up.
When it happens
Trigger: Fetching the source URL returns 4xx/5xx: 401/403 when the resource needs auth the desktop didn't send, 404 for an expired/missing link, 500 from a failing backend, or a CDN/signed-URL that has expired.
Common situations: Expired presigned/S3 URL; backend endpoint that requires a JWT the caller didn't attach; tool output that was cleaned up server-side; wrong URL passed by the caller.
Related errors
- Failed to load PDF for native print (${response.status})
- Sign up failed
- Cannot connect to server. Please check the server URL and en
- Login request timed out. Please check your network connectio
- Cannot resolve server address. Please check the server URL i
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/6c827ad75a5b85c2.
Report an issue: GitHub.