From 69648068a98eea166a275f8ddc688de207d35402 Mon Sep 17 00:00:00 2001 From: TonyTonyCoder11 Date: Sun, 5 Jul 2026 03:23:29 +0200 Subject: [PATCH] fix(dio): avoid FormatException on empty JSON body with a custom responseDecoder 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 #2285 (which only covered the fast, no-custom-decoder path); see #2279. --- dio/CHANGELOG.md | 5 +++- .../src/transformers/fused_transformer.dart | 4 ++- dio/test/transformer_test.dart | 28 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/dio/CHANGELOG.md b/dio/CHANGELOG.md index 09df23b45..ac9a403ba 100644 --- a/dio/CHANGELOG.md +++ b/dio/CHANGELOG.md @@ -5,7 +5,10 @@ See the [Migration Guide][] for the complete breaking changes list.** ## Unreleased -*None.* +- Fix `FusedTransformer` (the default transformer) throwing a `FormatException` + on an empty response body when a custom `responseDecoder` is set. It now + returns an empty result, consistent with `SyncTransformer` and + `BackgroundTransformer`. ## 5.10.0 diff --git a/dio/lib/src/transformers/fused_transformer.dart b/dio/lib/src/transformers/fused_transformer.dart index d65df1a21..18d0da639 100644 --- a/dio/lib/src/transformers/fused_transformer.dart +++ b/dio/lib/src/transformers/fused_transformer.dart @@ -92,7 +92,9 @@ class FusedTransformer extends Transformer { decodedResponse = null; } - if (isJsonContent && decodedResponse != null) { + if (isJsonContent && + decodedResponse != null && + decodedResponse.isNotEmpty) { // slow path decoder, since there was a custom decoder specified return jsonDecode(decodedResponse); } else if (customResponseDecoder != null) { diff --git a/dio/test/transformer_test.dart b/dio/test/transformer_test.dart index 733447c60..ab0ed8d33 100644 --- a/dio/test/transformer_test.dart +++ b/dio/test/transformer_test.dart @@ -384,6 +384,34 @@ void main() { expect(jsonResponse, null); }); + test( + 'empty JSON body with a custom responseDecoder does not throw', + () async { + // A custom decoder that returns the utf8-decoded body, which is an + // empty string for an empty response. Without guarding against an + // empty decoded string, the fused transformer would feed '' into + // jsonDecode and throw a FormatException, unlike SyncTransformer and + // BackgroundTransformer which both return an empty string here. + String decoder(List bytes, RequestOptions o, ResponseBody rb) => + utf8.decode(bytes, allowMalformed: true); + final transformer = FusedTransformer(); + final response = await transformer.transformResponse( + RequestOptions( + responseType: ResponseType.json, + responseDecoder: decoder, + ), + ResponseBody.fromBytes( + [], + 200, + headers: { + Headers.contentTypeHeader: [Headers.jsonContentType], + }, + ), + ); + expect(response, ''); + }, + ); + test('transform the request using urlencode', () async { final transformer = FusedTransformer();