Skip to content

fix: add transform timeout for background decoding#2530

Open
a1573595 wants to merge 4 commits into
cfug:mainfrom
a1573595:main
Open

fix: add transform timeout for background decoding#2530
a1573595 wants to merge 4 commits into
cfug:mainfrom
a1573595:main

Conversation

@a1573595
Copy link
Copy Markdown
Contributor

@a1573595 a1573595 commented May 28, 2026

Fixes background JSON decoding hanging indefinitely by adding a dedicated transformTimeout option.

The timeout is independent from receiveTimeout, so existing receive timeout semantics stay focused on network receive timing. By default transformTimeout is null, which means no transform timeout limit and preserves existing behavior unless users opt in.

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)
  • I have run the tests without failures
  • I have updated the CHANGELOG.md in the corresponding package

Additional context and info (if any)

This adds:

  • RequestOptions.transformTimeout
  • Options.transformTimeout
  • BaseOptions.transformTimeout
  • DioExceptionType.transformTimeout

When transformTimeout is set and response transformation exceeds the duration, Dio throws DioExceptionType.transformTimeout.

Existing users that do not set transformTimeout are unaffected. receiveTimeout continues to represent receiving response bytes/chunk timing and is no longer used to limit background JSON transformation.

Verified with:

  • dart analyze --fatal-infos in dio
  • dart analyze --fatal-infos in plugins/web_adapter
  • dart test --chain-stack-traces in dio

@a1573595 a1573595 requested a review from a team as a code owner May 28, 2026 00:26
@AlexV525
Copy link
Copy Markdown
Member

compute was copied from Flutter source. Why there will be timeouts when using it?

@a1573595
Copy link
Copy Markdown
Contributor Author

compute was copied from Flutter source. Why there will be timeouts when using it?

compute itself does not provide timeout semantics. It only completes when the spawned isolate returns, throws, or exits.

The timeout added here is Dio-specific: receiveTimeout already represents the request-level timeout configured by users, but large JSON decoding happens after the response bytes are received and runs inside Dio's internal background isolate. If that isolate never returns, the request can stay pending indefinitely.

Wrapping compute(...).timeout(...) outside is not enough because it only completes the outer Future; it cannot kill the spawned isolate. The isolate handle is only available inside compute_io.dart, so the timeout hook is added there to clean up the isolate properly.

This does not add a default timeout. It only applies when RequestOptions.receiveTimeout is configured.

@AlexV525
Copy link
Copy Markdown
Member

If that isolate never returns, the request can stay pending indefinitely.

What I don't understand is why we are expecting that some isolates will never return. That doesn't make sense and sounds like a Dart bug.

@a1573595
Copy link
Copy Markdown
Contributor Author

If that isolate never returns, the request can stay pending indefinitely.

What I don't understand is why we are expecting that some isolates will never return. That doesn't make sense and sounds like a Dart bug.

I see it differently. The timeout is not because we expect isolates to normally never return, or because we are working around a Dart isolate bug. It is a defensive bound around an async operation.

A Future is also allowed to never complete if the underlying work hangs, loops forever, waits on something that never responds, or gets stuck in a native/platform call. Awaiting it does not block the event loop, but the caller's async flow can still remain pending forever.

Isolates give us concurrency and memory isolation, but they do not guarantee task completion. If the isolate workload or message path gets stuck, the caller still needs a bounded failure mode. A timeout gives us that instead of letting the request hang indefinitely.

@AlexV525
Copy link
Copy Markdown
Member

AlexV525 commented May 28, 2026

I agreed with your point, though it might be better if we introduce a new type of error like transformTimeout and set the option separately.

@a1573595 a1573595 changed the title fix(dio): timeout background JSON decoding fix: add transform timeout for background decoding May 28, 2026
@a1573595
Copy link
Copy Markdown
Contributor Author

I agreed with your point, though it might be better if we introduce a new type of error like transformTimeout and set the option separately.

Updated. I added a separate transformTimeout option with DioExceptionType.transformTimeout.

receiveTimeout is no longer used for transformation timeout, and transformTimeout defaults to null, so existing behavior remains unchanged unless explicitly configured.

@a1573595
Copy link
Copy Markdown
Contributor Author

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
dio/lib/src/compute/compute_io.dart 🟠 69.81% 🟠 75% 🟢 5.19%
dio/lib/src/dio_exception.dart 🟢 89.39% 🟢 90% 🟢 0.61%
dio/lib/src/interceptors/log.dart 🟢 76.56% 🟢 76.92% 🟢 0.36%
dio/lib/src/options.dart 🟢 90.16% 🟢 87.08% 🔴 -3.08%
dio/lib/src/transformers/fused_transformer.dart 🟢 93.33% 🟢 93.88% 🟢 0.55%
Overall Coverage 🟢 88.88% 🟢 88.67% 🔴 -0.21%
Minimum allowed coverage is 0%, this run produced 88.67%

Thanks for pointing this out.

I added tests for the new transformTimeout option paths in options.dart, and also made compute_test.dart run on both VM and browser so the web wrapper is covered too.

@github-actions
Copy link
Copy Markdown
Contributor

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
dio/lib/src/compute/compute_io.dart 🟠 69.81% 🟢 75.76% 🟢 5.95%
dio/lib/src/dio_exception.dart 🟢 89.39% 🟢 90% 🟢 0.61%
dio/lib/src/interceptors/log.dart 🟢 76.56% 🟢 76.92% 🟢 0.36%
dio/lib/src/options.dart 🟢 90.16% 🟢 90.91% 🟢 0.75%
dio/lib/src/transformers/fused_transformer.dart 🟢 93.33% 🟢 93.88% 🟢 0.55%
Overall Coverage 🟢 88.88% 🟢 89.09% 🟢 0.21%

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

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