NousResearch/hermes-agent · error · Error
File uploads are not supported against OAuth-gated remote ba
Error message
File uploads are not supported against OAuth-gated remote backends yet.
What it means
The desktop REST bridge supports three auth modes; for authMode 'oauth' it rides Electron's net stack with JSON headers bound to the OAuth session partition, and multipart file upload bodies are not implemented on that path. When a request carries request.upload against an OAuth-gated remote backend, it throws this explicit error instead of silently corrupting the upload. It is a known capability gap, deliberately failing loudly.
Source
Thrown at apps/desktop/electron/main.ts:10879
const timeoutMs = resolveTimeoutMs(request?.timeoutMs, DEFAULT_FETCH_TIMEOUT_MS)
const requestPath = pathWithGlobalRemoteProfile(request.path, profile, profileRouteOptions(profile))
const url = `${connection.baseUrl}${requestPath}`
// OAuth gateways authenticate REST via EITHER a native bearer token
// (cookieless RFC 8252 flow) OR the HttpOnly session cookie held in the OAuth
// partition. Prefer the native bearer when present (mirroring
// mintGatewayWsTicket): the native flow never sets a cookie, so routing an
// oauth-mode REST call through the cookie-only path returns 401 no_cookie even
// though a valid bearer is held. Cookie mode rides Electron's net stack bound
// to the OAuth partition so the cookie attaches automatically. Token/local
// modes keep using the static session-token header.
if (connection.authMode === 'oauth') {
// The OAuth path rides electron.net with JSON headers; multipart isn't
// wired there. Fail loudly rather than corrupting the upload.
if (request?.upload) {
throw new Error('File uploads are not supported against OAuth-gated remote backends yet.')
}
// Native bearer first (cookieless). ensureNativeAccessToken transparently
// refreshes a near-expiry AT via /auth/native/refresh; a null return means
// no native session (resolveOauthRestAuth then selects the cookie path).
const nativeAt = await ensureNativeAccessToken(connection.baseUrl).catch(() => null)
const restAuth = resolveOauthRestAuth(nativeAt)
if (restAuth.kind === 'bearer') {
return fetchJson(url, null, {
method: request?.method,
body: request?.body,
timeoutMs,
bearer: restAuth.token
})
}
return fetchJsonViaOauthSession(url, {View on GitHub (pinned to c896c09c42)
Solutions
- Connect to that backend with a token (API token) instead of OAuth so uploads take the static-token fetch path
- Upload against a local backend or via the CLI/gateway directly, then reference the uploaded artifact
- If you own the code: implement multipart on the electron.net OAuth path (cookie or native bearer) — until then keep gating uploads on authMode !== 'oauth'
- In renderer UI, disable/hide the attach button when connection.authMode === 'oauth' and show 'uploads unsupported on OAuth remotes'
Example fix
// before (renderer)
await ipc.invoke('hermes:api', { path: '/api/upload', upload: file })
// after
if (connection.authMode === 'oauth') throw new Error('Uploads unsupported on OAuth remote — connect with a token')
await ipc.invoke('hermes:api', { path: '/api/upload', upload: file }) Defensive patterns
Strategy: validation
Validate before calling
if (request?.upload && connection.authMode === 'oauth') {
throw new Error('Upload not available on OAuth remote — connect with a token or use a local backend')
} Type guard
function uploadSupported(connection: { authMode: string }): boolean {
return connection.authMode === 'token' || connection.authMode === 'local'
} Try / catch
try { await api(path, { upload }) } catch (e) { if (e instanceof Error && e.message.includes('not supported against OAuth-gated')) notify('Switch to a token connection to upload files') else throw e } Prevention
- Gate the attach/upload UI on authMode !== 'oauth'
- Prefer token-auth connections when file uploads are part of the workflow
- Track the OAuth multipart gap as a feature flag rather than discovering it in production
When it happens
Trigger: Any renderer API call that sets request.upload (file attachment) while the active connection config has authMode 'oauth' — e.g. attaching a file to a chat message against a Hermes Cloud/OAuth remote backend.
Common situations: Users on OAuth-gated remote (cloud) backends trying the file-attach feature that works fine against token/local connections; code paths that assume the upload path is auth-mode-agnostic.
Related errors
- Gateway rejected native login: ${error}${desc ? ` (${desc})`
- Loopback callback missing authorization code
- Loopback callback state mismatch (possible CSRF)
- Gateway token response missing access_token
- Stored token set missing accessToken
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/6b75a74b6d588e20.
Report an issue: GitHub.