Skip to content

fix(dio): avoid FormatException on empty JSON body with a custom responseDecoder#2550

Open
tonytonycoder11 wants to merge 1 commit into
cfug:mainfrom
tonytonycoder11:fix/fused-transformer-empty-json-custom-decoder
Open

fix(dio): avoid FormatException on empty JSON body with a custom responseDecoder#2550
tonytonycoder11 wants to merge 1 commit into
cfug:mainfrom
tonytonycoder11:fix/fused-transformer-empty-json-custom-decoder

Conversation

@tonytonycoder11

Copy link
Copy Markdown

Problem

FusedTransformer is the default Transformer. When a request sets a custom responseDecoder and the server returns an empty body with a JSON content-type (a normal 200/204/304), the transformer's slow path runs jsonDecode(''), throwing FormatException: Unexpected end of input.

SyncTransformer and BackgroundTransformer handle the identical input gracefully — they guard the empty string and return it (which dio normalizes to null for typed JSON responses). Only the default transformer crashes.

This is the residual half of #2279: PR #2285 fixed the empty-body → null behavior for the fast path (no custom decoder) but never guarded the custom-decoder slow path.

Fix

Add an isNotEmpty check to the slow-path guard so an empty decoded body falls through to the existing return decodedResponse; branch, matching SyncTransformer / BackgroundTransformer:

if (isJsonContent && decodedResponse != null && decodedResponse.isNotEmpty) {
  return jsonDecode(decodedResponse);
} else if (customResponseDecoder != null) {
  return decodedResponse;
}

Non-empty malformed bodies still throw, exactly as before and as the sibling transformers do (the fix is intentionally scoped to match their isNotEmpty-only guard).

New Pull Request Checklist

  • I have read the Documentation
  • I have searched for a similar pull request in the project and found none
  • I have updated this branch with the latest main branch to avoid conflicts (via merge from master or rebase)
  • I have added the required tests to prove the fix/feature I'm adding
  • I have updated the documentation (if necessary) — n/a, no public API change
  • I have run the tests without failures
  • I have updated the CHANGELOG.md in the corresponding package

Additional context and info (if any)

Reproduction (no network needed), calling the default transformer directly:

String decoder(List<int> b, RequestOptions o, ResponseBody rb) =>
    utf8.decode(b, allowMalformed: true);

await FusedTransformer().transformResponse(
  RequestOptions(responseType: ResponseType.json, responseDecoder: decoder),
  ResponseBody.fromBytes([], 200, headers: {
    Headers.contentTypeHeader: [Headers.jsonContentType],
  }),
);
// before: throws FormatException: Unexpected end of input
// after:  returns '' (consistent with SyncTransformer / BackgroundTransformer)

The added regression test (dio/test/transformer_test.dart"empty JSON body with a custom responseDecoder does not throw") fails without the fix and passes with it.

…onseDecoder

FusedTransformer (the default transformer) fed an empty decoded body into
jsonDecode when a custom responseDecoder was set and the response had a JSON
content-type with an empty body (e.g. 200/204/304). jsonDecode('') throws
FormatException: Unexpected end of input.

SyncTransformer and BackgroundTransformer already guard the empty case with
isNotEmpty and return an empty result (normalized to null downstream for typed
JSON). Align FusedTransformer's custom-decoder slow path with the same guard.

Completes the graceful empty-body handling from cfug#2285 (which only covered the
fast, no-custom-decoder path); see cfug#2279.
@tonytonycoder11 tonytonycoder11 requested a review from a team as a code owner July 5, 2026 01:24
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
dio/lib/src/transformers/fused_transformer.dart 🟢 93.88% 🟢 94% 🟢 0.12%
Overall Coverage 🟢 85.55% 🟢 85.56% 🟢 0.01%

Minimum allowed coverage is 0%, this run produced 85.56%

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant