Skip to content

Conversation

@anthony-murphy-agent
Copy link
Contributor

@anthony-murphy-agent anthony-murphy-agent commented Jan 9, 2026

Summary

  • Fix unhandled promise rejections in PrefetchDocumentStorageService fire-and-forget calls
  • Add unit test for getSnapshotTree failure scenario

Problem

This is a follow-up to PR #26151. After that fix was deployed, telemetry showed 349 uncaughtException errors from ShreddedSummaryDocumentStorageService.readBlob in test-service-load tinylicious tests.

Root cause: PR #26151 attached .catch() to the inner promise in cachedRead(), but that only handles ONE promise chain. The fire-and-forget callers create a DIFFERENT chain:

  1. void this.cachedRead(blob) - cachedRead is async, so it returns a NEW promise wrapping the inner promise. The .catch() on the inner promise doesn't prevent unhandled rejection on the async function's returned promise.

  2. void p.then(...) in getSnapshotTree() - creates a derived promise that also rejects when p rejects.

Solution

Fix all three fire-and-forget patterns:

// Before (inner promise handled, but async wrapper not):
void this.cachedRead(blob);

// After (handles the async function's returned promise):
this.cachedRead(blob).catch(() => {});
// Before (derived promise not handled):
void p.then((tree) => this.prefetchTree(tree));

// After (handles the derived promise):
void p.then(...).catch(() => {});

Test plan

  • New unit test for getSnapshotTree failure scenario
  • All 5 PrefetchDocumentStorageService tests pass
  • Build passes

🤖 Generated with Claude Code

Co-Authored-By: anthony-murphy [email protected]

@anthony-murphy
Copy link
Contributor

/azp run Build - api-markdown-documenter, Build - benchmark-tool, Build - build-common, Build - build-tools, Build - client packages, Build - common-utils, Build - eslint-config-fluid, Build - eslint-plugin-fluid, Build - protocol-definitions, Build - test-tools

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@anthony-murphy
Copy link
Contributor

/azp run repo-policy-check, server-gitrest, server-gitssh, server-historian, server-routerlicious

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes an unhandled promise rejection bug in PrefetchDocumentStorageService.getSnapshotTree() by adding a .catch() handler to the fire-and-forget prefetch pattern. This is a follow-up to PR #26151, which fixed a similar issue in cachedRead() but missed this code path.

  • Adds .catch() handler to prevent unhandled rejections when getSnapshotTree() fails
  • Adds unit test to verify the fix and ensure errors are still properly propagated to callers

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
packages/loader/driver-utils/src/prefetchDocumentStorageService.ts Adds .catch() handler to fire-and-forget prefetch pattern in getSnapshotTree() to prevent unhandled rejections while still propagating errors to callers
packages/loader/driver-utils/src/test/prefetchDocumentStorageService.spec.ts Adds mock failure flags and test case to verify getSnapshotTree() failures don't cause unhandled rejections

…orget calls

Fix three fire-and-forget patterns that cause unhandled rejections:

1. getSnapshotTree(): `void p.then(...)` creates a derived promise that
   rejects when p rejects. Added `.catch()` to the promise chain.

2. prefetchTree()/prefetchTreeCore(): `void this.cachedRead(blob)` discards
   the async function's returned promise. Since cachedRead is async, it
   returns a separate promise chain from the inner prefetchedBlobPFromStorage.
   The existing `.catch()` inside cachedRead only handles the inner promise,
   not the async function's returned promise. Changed to
   `this.cachedRead(blob).catch(() => {})`.

The root cause: attaching `.catch()` to a promise P only prevents unhandled
rejection for P's chain. When an async function returns P, the function's
returned promise is a DIFFERENT chain that also rejects - and needs its own
`.catch()` handler.

This is a follow-up to PR microsoft#26151 which only fixed the inner promise in
cachedRead but didn't fix the callers' fire-and-forget patterns.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: anthony-murphy <[email protected]>
@anthony-murphy-agent anthony-murphy-agent force-pushed the fix/prefetch-unhandled-rejection branch from 907fe32 to a3e582d Compare January 9, 2026 20:40
Address review comment - void is no longer needed since .catch() handles
the promise rejection, so there's no floating promise issue.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: anthony-murphy <[email protected]>
@anthony-murphy-agent
Copy link
Contributor Author

Azure Pipelines commands (copy and post separately, limit of 10 at a time):

/azp run Build - api-markdown-documenter, Build - benchmark-tool, Build - build-common, Build - build-tools, Build - client packages, Build - common-utils, Build - eslint-config-fluid, Build - eslint-plugin-fluid, Build - protocol-definitions, Build - test-tools
/azp run repo-policy-check, server-gitrest, server-gitssh, server-historian, server-routerlicious

@anthony-murphy
Copy link
Contributor

/azp run Build - api-markdown-documenter, Build - benchmark-tool, Build - build-common, Build - build-tools, Build - client packages, Build - common-utils, Build - eslint-config-fluid, Build - eslint-plugin-fluid, Build - protocol-definitions, Build - test-tools

@anthony-murphy anthony-murphy requested a review from Copilot January 9, 2026 20:46
@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@anthony-murphy
Copy link
Contributor

/azp run repo-policy-check, server-gitrest, server-gitssh, server-historian, server-routerlicious

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@anthony-murphy anthony-murphy enabled auto-merge (squash) January 9, 2026 20:52
@anthony-murphy anthony-murphy merged commit 6f218b3 into microsoft:main Jan 9, 2026
34 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.

3 participants