From 43bb14bf874cf97c2be71579f1b9e5331f9c7266 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Wed, 13 May 2026 02:03:24 +0000 Subject: [PATCH] action: strip Content-Type on body-less apiFetch requests (#692) (#694) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * action: strip Content-Type on body-less apiFetch requests (#692) Vercel's Next.js lambda adapter (Next 16.1.x) attempts to decode a request body when Content-Type is set and throws `SyntaxError: Unexpected end of data` before delegating to the route handler, returning a 500. Hit /run-context exclusively because it was the only body-less GET that sent `Content-Type: application/json`. - Drop `Content-Type: application/json` from the GET in `action/utils/runContext.ts` (meaningless on a body-less request). - Defensively strip any `content-type` header in `action/utils/apiFetch.ts` when no body is present so future callers can't reintroduce this. * apiFetch: soften comment — empirical observation, RFC 9110 §8.3 framing --- utils/apiFetch.ts | 12 ++++++++++++ utils/runContext.ts | 1 - 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/utils/apiFetch.ts b/utils/apiFetch.ts index b264491..2fc7b98 100644 --- a/utils/apiFetch.ts +++ b/utils/apiFetch.ts @@ -37,6 +37,18 @@ export async function apiFetch(options: ApiFetchOptions): Promise { headers["x-vercel-protection-bypass"] = bypassSecret; } + // never send Content-Type on body-less requests. empirically, Vercel's + // Next.js lambda adapter (Next 16.1.x) throws `SyntaxError: Unexpected + // end of data` before delegating to the route handler — returning a 500 — + // when Content-Type is set but no body is present. exact mechanism is + // unverified (minified runtime frame), but Content-Type on a body-less + // request has no defined semantics per RFC 9110 §8.3 anyway. see #692. + if (!options.body) { + for (const key of Object.keys(headers)) { + if (key.toLowerCase() === "content-type") delete headers[key]; + } + } + log.debug(`api fetch: ${options.method ?? "GET"} ${url.pathname}`); const init: RequestInit = { diff --git a/utils/runContext.ts b/utils/runContext.ts index 5c7f747..9235fb7 100644 --- a/utils/runContext.ts +++ b/utils/runContext.ts @@ -88,7 +88,6 @@ export async function fetchRunContext(params: { try { const headers: Record = { Authorization: `Bearer ${params.token}`, - "Content-Type": "application/json", }; if (params.oidcToken) { headers["X-GitHub-OIDC-Token"] = params.oidcToken;