Skip to content

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

Merged
AlexV525 merged 2 commits into
cfug:mainfrom
tonytonycoder11:fix/fused-transformer-empty-json-custom-decoder
Jul 14, 2026
Merged

fix(dio): avoid FormatException on empty JSON body with a custom responseDecoder#2550
AlexV525 merged 2 commits into
cfug:mainfrom
tonytonycoder11:fix/fused-transformer-empty-json-custom-decoder

Conversation

@tonytonycoder11

Copy link
Copy Markdown
Contributor

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
…-empty-json-custom-decoder

# Conflicts:
#	dio/CHANGELOG.md

@AlexV525 AlexV525 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution!

@github-actions

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.72% 🟢 85.73% 🟢 0.01%

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

@AlexV525 AlexV525 merged commit 92e9efa into cfug:main Jul 14, 2026
5 checks passed
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.

2 participants