Skip to content

Fix: request hang on a failing Future payload - #193

Merged
gmpassos merged 1 commit into
masterfrom
fix/future-payload-error-zone
Oct 27, 2026
Merged

gmpassos merged 1 commit into
masterfrom
fix/future-payload-error-zone

Conversation

@gmpassos

Copy link
Copy Markdown
Contributor

Stacked on #192 (base feat/brotli). Merge #192 first; this PR's diff is only the fix.

Bug

A route whose APIResponse has a Future payload that fails never gets a response: the request hangs until the client gives up. It already happens on master, so this PR doesn't depend on Brotli.

routes.any('x', (request) => APIResponse.ok(
  Future<Object>.delayed(Duration(milliseconds: 1), () => throw StateError('boom')),
));

The server only logs Uncaught asynchronous error while calling: APIRequest#... and never answers.

Cause

APIRoot runs each call in its own error Zone (handleUncaughtError). The route creates the payload Future inside that zone, and APIServer.resolveBody listens to it (then(..., onError: ...)) from outside. Dart never delivers a Future error to a listener in a different error zone. It's reported as uncaught in the zone where the Future was created instead. So the onError that builds the 500 response never runs, and the future it returns never completes.

Fix

  • APIRoot records the call zone of a Future payload: APIResponse.payloadZone.
  • resolveBody listens to the payload inside that zone (APIResponse.runInPayloadZone), so it receives the error. Its result is a value (an error APIResponse on failure), and values cross zones normally. The request gets a 500, compressed like any other response.
  • A payload that isn't a Future has no payloadZone, and nothing changes for it.

Tests

  • HTTP: a failing Future payload returns 500, uncompressed without Accept-Encoding, and compressed with br and gzip.
  • APIRoot.call and then resolveBody: payloadZone is set, the error APIResponse is received, a successful Future payload still resolves, and a payload that isn't a Future has no zone.
  • Both tests time out with the fix disabled.
  • Local runs: dart format, dart analyze --fatal-infos and the full suite, including the Docker DB suites, all pass, and so does ensure_build.

🤖 Generated with Claude Code

An `APIResponse` with a `Future` payload that fails never got a response:
the `Future` is created inside the `APIRoot.call` error `Zone`, and errors
don't cross error zones, so `APIServer.resolveBody` (listening outside it)
never received the error (only logged as an uncaught asynchronous error).

- `APIRoot` records the call `Zone` of a `Future` payload
  (`APIResponse.payloadZone`), and `resolveBody` listens to it there
  (`APIResponse.runInPayloadZone`). Its result, an error `APIResponse` on
  failure, is a value: delivered to any zone. The request gets a `500`.
- Tests: HTTP (`500`, also compressed) and `resolveBody` with the
  `payloadZone`. Both time out without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gmpassos
gmpassos changed the base branch from feat/brotli to master October 27, 2026 01:14
@gmpassos
gmpassos merged commit 0a6d4ce into master Oct 27, 2026
9 checks passed
@codecov

codecov Bot commented Oct 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.66%. Comparing base (d357398) to head (ad67b1c).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #193      +/-   ##
==========================================
+ Coverage   95.61%   95.66%   +0.04%     
==========================================
  Files          75       75              
  Lines       25747    25754       +7     
==========================================
+ Hits        24619    24637      +18     
+ Misses       1128     1117      -11     
Flag Coverage Δ
unittests 95.66% <100.00%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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