From d50576786dc6ff85f609e63740631b11610d22a7 Mon Sep 17 00:00:00 2001 From: Kyzcreig <9063726+Kyzcreig@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:23:09 -0700 Subject: [PATCH] fix(desktop): drop manual Content-Length in OAuth net.request (ERR_INVALID_ARGUMENT) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Electron's net.request (ClientRequest, Chromium net stack) treats Content-Length as a protected header it computes itself from request.write(body). Setting it explicitly throws net::ERR_INVALID_ARGUMENT and the request fails before leaving the app. This only affected the OAuth/remote path (fetchJsonViaOauthSession via electronNet) on body-bearing POSTs — most visibly POST /api/model/set from Settings -> Models 'Apply', which surfaced as "Error invoking remote method 'hermes:api': Error: net::ERR_INVALID_ARGUMENT". GET and bodyless POSTs were unaffected, and the local/token path (fetchJson via Node http) tolerates a manual Content-Length, so it regressed only for remote-backend OAuth users. Verified empirically with a headless Electron net.request probe: WITH Content-Length -> net::ERR_INVALID_ARGUMENT WITHOUT Content-Length -> net::ERR_UNSAFE_PORT (header validation passed) Introduced in #39921. --- apps/desktop/electron/main.cjs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/desktop/electron/main.cjs b/apps/desktop/electron/main.cjs index b42913093d7c..339ddc6cbfb2 100644 --- a/apps/desktop/electron/main.cjs +++ b/apps/desktop/electron/main.cjs @@ -3478,7 +3478,15 @@ function fetchJsonViaOauthSession(url, options = {}) { redirect: 'follow' }) request.setHeader('Content-Type', 'application/json') - if (body) request.setHeader('Content-Length', String(body.length)) + // NOTE: do NOT set Content-Length manually here. Electron's net.request + // (ClientRequest, Chromium net stack) treats Content-Length as a protected + // header it computes itself from request.write(body); setting it explicitly + // throws `net::ERR_INVALID_ARGUMENT` and the whole request fails before it + // leaves the app. This only bites the OAuth/remote path (electronNet) on + // body-bearing POSTs — e.g. POST /api/model/set from Settings → Models — while + // GET and bodyless POSTs are unaffected. The local/token path (fetchJson, Node + // http) tolerates a manual Content-Length, which is why this regressed only + // for remote-backend OAuth users. let timedOut = false const timer = setTimeout(() => {