Skip to content

Fetch client returns JSON as string when Firefox exposes a comma-separated Content-Type #2805

Description

@mirismaili

Summary

The generated Fetch client decides whether to call response.json() or response.text() by checking whether the Content-Type header starts with one of the known JSON media types:

const jsonTypes = ['application/json', 'application/problem+json'];
const isJSON = jsonTypes.some(type =>
  contentType.toLowerCase().startsWith(type),
);

In some Firefox sessions, Response.headers.get('content-type') can intermittently expose a comma-separated value containing an unexpected media type before the actual one, for example:

text/html,application/json

When the response body is JSON, the generated client consequently calls response.text() and returns a string instead of the expected parsed object.

Mozilla Bugzilla has a report showing the same browser-level symptom: Firefox returned a comma-separated Content-Type containing an unexpected value followed by the actual response media type:

https://bugzilla.mozilla.org/show_bug.cgi?id=2047399

That report was resolved as WORKSFORME after the intermittent behavior could no longer be reproduced, so applications may still need a defensive workaround for affected Firefox versions or sessions.

Deterministic reproduction of the generated-client behavior

The browser bug itself is intermittent, but the behavior of the generated parser can be reproduced independently:

const response = new Response(JSON.stringify({ ok: true }), {
  status: 200,
  headers: {
    'Content-Type': 'text/html,application/json',
  },
});

const result = await getResponseBody(response);

console.log(result);        // '{"ok":true}'
console.log(typeof result); // 'string'

Expected behavior

There should be a straightforward supported way for generated Fetch clients to defensively recognize JSON when a browser exposes a comma-separated header containing a JSON media type, without editing generated files.

Possible approaches could include:

  • treating each comma-separated media-type candidate independently;
  • exposing a small response-body parser hook or response transformer;
  • allowing the JSON content-type predicate to be overridden.

Current workaround

The existing --request <custom-request> option can work around the problem, but it requires copying and maintaining the complete request implementation only to customize this small parsing decision. A narrower extension point or built-in defensive handling would be significantly easier to maintain.

This is related to, but not fully covered by, #386: startsWith() handles media-type parameters when JSON is first, but does not handle an unexpected media type appearing before the JSON type.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions