NousResearch/hermes-agent · error
Could not fetch image: ${response.status}
Error message
Could not fetch image: ${response.status} What it means
Thrown by startBrowserDownload() in apps/desktop/src/hooks/use-image-download.ts:28 — the fallback path of useImageDownload — when `fetch(src)` resolves with a non-OK HTTP status. The desktop first tries the IPC handler `hermes:saveImageFromUrl`; when that's missing (older shell / web preview) it fetches the image URL in the renderer and triggers a browser-style download, so any 4xx/5xx from the image host surfaces as this error.
Source
Thrown at apps/desktop/src/hooks/use-image-download.ts:28
try {
return new URL(src, window.location.href).pathname.split('/').filter(Boolean).pop() || 'image'
} catch {
return src.split(/[\\/]/).filter(Boolean).pop() || 'image'
}
}
function isMissingIpcHandler(error: unknown): boolean {
const message = error instanceof Error ? error.message : typeof error === 'string' ? error : ''
return message.includes("No handler registered for 'hermes:saveImageFromUrl'")
}
async function startBrowserDownload(src: string) {
const response = await fetch(src)
if (!response.ok) {
throw new Error(`Could not fetch image: ${response.status}`)
}
const blobUrl = URL.createObjectURL(await response.blob())
const link = document.createElement('a')
link.href = blobUrl
link.download = imageFilename(src)
link.rel = 'noopener noreferrer'
document.body.appendChild(link)
link.click()
link.remove()
window.setTimeout(() => URL.revokeObjectURL(blobUrl), 30_000)
}
/** Save an image to disk via the desktop IPC bridge, falling back to a browser
* download when the handler is unavailable (older shell / web preview). */
export function useImageDownload(src?: string) {
const { t } = useI18n()
const copy = t.desktopView on GitHub (pinned to c896c09c42)
Solutions
- Verify the image URL still loads (open it directly, re-check for expired signatures) and retry the download after re-fetching a fresh URL.
- For auth-protected images, prefer the desktop IPC path (update the desktop shell so saveImageFromUrl exists and fetches via the main process with proper auth).
- If the source is a data: URL or local path, ensure it is converted to a fetchable/exportable form before download.
- Handle the error with the hook's notifyError path and offer re-generation of the image.
Example fix
// before
const response = await fetch(src)
if (!response.ok) throw new Error(`Could not fetch image: ${response.status}`)
// after — refresh a stale signed URL once, then retry
let url = src
let response = await fetch(url)
if (response.status === 403 || response.status === 404) {
url = await refreshImageUrl(src)
response = await fetch(url)
}
if (!response.ok) throw new Error(`Could not fetch image: ${response.status}`) Defensive patterns
Strategy: retry
Validate before calling
async function isFetchable(src: string): Promise<boolean> {
try { const r = await fetch(src, { method: 'HEAD' }); return r.ok } catch { return false }
} Try / catch
try {
await startBrowserDownload(src)
} catch (e) {
if (e instanceof Error && /^Could not fetch image: (401|403|404)$/.test(e.message)) {
const fresh = await refreshImageUrl(src) // re-sign / re-host
if (fresh) await startBrowserDownload(fresh)
else notifyError(e)
} else throw e
} Prevention
- Save images promptly before signed URLs expire
- Prefer the desktop IPC saveImageFromUrl path (auth handled in main process) over renderer fetch
- HEAD-check questionable URLs before showing a download button
- Don't leave download handlers with no error UI — surface status codes to the user
When it happens
Trigger: Image src returning 404 (expired signed URL, deleted file, typo); 401/403 for authenticated/CDN URLs requiring headers the renderer fetch cannot attach; 500 from the origin server; CORS-configured host returning an error status (fetch succeeds but response.ok false).
Common situations: An image-generation result URL that expired before the user clicked save; a gateway-hosted image on a remote backend whose auth token isn't propagated to the raw fetch; offline/proxy failures; the message referencing a local file path that the HTTP host doesn't serve.
Related errors
- Gateway did not return a WS ticket.
- Reached the gateway over HTTP, but the live WebSocket (/api/
- Failed to download {url}: {exc}
- Failed to download {}: HTTP {} from {}
- gh pr create failed (is gh installed and authenticated?)
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/aade7d909a59d3c5.
Report an issue: GitHub.