NousResearch/hermes-agent · error
text || HTTP ${res.status}
Error message
text || HTTP ${res.status} What it means
The image upload POST to /api/chat/image-upload returned a non-OK HTTP status. The error prefers the server's response body text (which usually carries the backend's own error message) and falls back to `HTTP <status>` only when the body is empty. This is the same pattern as the dashboard's generic authedFetch error, applied to the chat image endpoint.
Source
Thrown at web/src/lib/chatImagePaste.ts:156
const file =
blob instanceof File
? blob
: new File([blob], filename, { type: mime });
const dataUrl = await fileToDataUrl(file);
const qs = profile ? `?profile=${encodeURIComponent(profile)}` : "";
const res = await authedFetch(`/api/chat/image-upload${qs}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
data_url: dataUrl,
filename,
}),
});
if (!res.ok) {
const text = await res.text().catch(() => res.statusText);
throw new Error(text || `HTTP ${res.status}`);
}
const uploaded = (await res.json()) as ChatImageUploadResult;
if (!uploaded?.path) {
throw new Error("image upload did not return a path");
}
return uploaded;
}
View on GitHub (pinned to c896c09c42)
Solutions
- Read the server text in the error message — it usually names the exact backend cause (invalid data URL, payload too large, auth required).
- For 401: reload the dashboard to refresh auth, then paste again.
- For 413: raise the proxy's request body limit or send a smaller image (see the max-MB error).
- For 500: check gateway logs for the write failure (permissions/disk) in the upload target directory.
Defensive patterns
Strategy: try-catch
Try / catch
try {
const { path } = await uploadChatImage(blob, profile)
} catch (err) {
const msg = String(err)
if (msg.startsWith('401') || msg.startsWith('403')) { location.reload(); return }
if (msg.startsWith('413')) { toast('Image too large for the server'); return }
toast(`Upload failed: ${msg}`)
} Prevention
- Surface the server-provided body text verbatim — it names the real cause.
- Keep proxy body limits aligned with the client-side MAX_IMAGE_BYTES.
- Handle auth expiry (401) by reloading rather than retrying the same request.
When it happens
Trigger: Uploading a data_url whose base64 is corrupt or whose mime is disallowed (400), uploading when the session expired (401), hitting a server-side body-size limit (413), or the gateway erroring while writing the upload to disk (500).
Common situations: Session cookie expired between page load and paste; reverse proxy (nginx/caddy) with a smaller client_max_body_size than the dashboard's limit; disk-full or permission errors in the gateway's upload directory.
Related errors
- ${res.status}: ${text}
- clipboard image is empty
- image too large (max ${mb} MB)
- image upload did not return a path
- Missing URL
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/187bd343c81a2378.
Report an issue: GitHub.