Skip to content

Bug: Response Content-Type header is overwritten by mapResponseBody() #1817

@akapug

Description

@akapug

Summary

When using elide serve with a fetch handler that returns a Response with a custom Content-Type header, the header is overwritten based on the body type, ignoring the user-specified value.

Regression Source

This bug was introduced in PR #1736 ("feat: new server engine", merged Nov 8, 2025).

The mapResponseBody() function was added as part of the new server engine, and it unconditionally overwrites Content-Type:

// From PR #1736 diff - packages/graalvm/.../FetchIntrinsic.kt
+  private fun mapResponseBody(
+    value: Value?,
+    headers: FetchMutableHeaders,
+    ...
+    value.isString -> {
+      val bytes = value.asString().toByteArray(StandardCharsets.UTF_8)
+      headers.set("Content-Type", "text/plain")  // ← Bug: unconditionally overwrites

The new fetch handler pattern's body mapping logic doesn't respect user-specified headers.

Expected Behavior

export default async function fetch(request: Request) {
  return new Response("<h1>Hello</h1>", { 
    headers: { "Content-Type": "text/html; charset=utf-8" }
  });
}

Should return Content-Type: text/html; charset=utf-8

Actual Behavior

Returns Content-Type: text/plain because mapResponseBody() unconditionally overwrites the header.

Root Cause

In FetchIntrinsic.kt, the mapResponseBody() function always calls headers.set("Content-Type", ...) based on body type:

  • String body → text/plain
  • Buffer → application/octet-stream
  • Object → application/json

This overwrites any user-specified Content-Type because headers are passed to mapResponseBody() after being populated from user options, and then overwritten.

Proposed Fix

Check if Content-Type is already set before overwriting:

// Before (bug):
headers.set("Content-Type", "text/plain")

// After (fix):
if (!headers.has("Content-Type")) headers.set("Content-Type", "text/plain")

Verification

Confirmed via curl that custom headers ARE applied but Content-Type is overwritten:

< X-Custom: test-value     ← Works!
< Content-Type: text/plain ← Overwritten (should be text/html)

Affected Versions

Testing revealed:

  • beta9: Fetch handler pattern not yet available (server doesn't start)
  • beta10, beta11-rc1 through rc3: Have fetch handler but with Content-Type bug

Since PR #1736 introduced both the fetch handler pattern AND the mapResponseBody() function, all versions with fetch handler support have this bug.

No Workaround Available

Environment

  • Elide version: 1.0.0-beta11-rc3
  • OS: Linux
  • Runtime: Native

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingmodule:graalvmModules, changes, and issues relating to GraalVM

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions