Skip to content

[WIP]: render-time error recovery#802

Draft
tmarmer wants to merge 17 commits intoerror-controllerfrom
render-error-handling
Draft

[WIP]: render-time error recovery#802
tmarmer wants to merge 17 commits intoerror-controllerfrom
render-error-handling

Conversation

@tmarmer
Copy link
Contributor

@tmarmer tmarmer commented Feb 12, 2026

  • Update captureError to fail the player state if no error transition is available.
  • Implement render-time error recovery functionality
    • iOS: Errors are handled during deserialization of asset data. Asset render functions do not support throwing so actual render-time issues do not need to be caught
    • Android: Errors are caught for each asset type and bubble up according to their needs.
      • RenderableAsset: Bubbles up to PlayerFragment error handling which calls PlayerViewModel.fail which now uses the error controller.
      • SuspendableAsset: Errors caught in the coroutine exception handler are sent to the error controller (except for cancellation exceptions)
      • ComposableAsset: Errors thrown during deserialization are sent to the error controller.
    • React: Top level ErrorBoundary on react player catches and sends errors to the ErrorController.
  • Add PlayerErrorMetadata interface for errors to implement to include info required for the error controller to capture errors
  • Add errors for specific areas where it can be brought to the error controller
    • AssetRenderError updated to include metadata required for the error controller
    • ResolverError added for errors thrown during computeTree
  • Update async-node plugin to make use of the above to call its onAsyncNodeError hook and replace broken content

Notes For Reviewers

  • Tests are WIP and will be added
  • iOS unfinished, should not be reviewed
  • There is a failing test in the async node plugin. This is because the error controller doesn't fail the player state. Need to update the error controller or change the test to not care about player state.

TODO:

  • Add iOS implementation
    • implement error recovery logic
    • add errors with metadata
  • Add/Modify central error types across platforms. Allow for metadata, error type and severity in those errors and read those properties.
  • Add tests
  • Code cleanup

Change Type (required)

Indicate the type of change your pull request is:

  • patch
  • minor
  • major
  • N/A

Does your PR have any documentation updates?

  • Updated docs
  • No Update needed
  • Unable to update docs
📦 Published PR as canary version: 0.15.1--canary.802.31569

Try this version out locally by upgrading relevant packages to 0.15.1--canary.802.31569

@tmarmer tmarmer requested review from a team as code owners February 12, 2026 22:16
@codecov
Copy link

codecov bot commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 71.55050% with 200 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.55%. Comparing base (07bcf2a) to head (7f4d5f0).

Files with missing lines Patch % Lines
...nce-assets/core/src/plugins/chat-ui-demo-plugin.ts 40.38% 31 Missing ⚠️
react/subscribe/src/index.tsx 11.76% 30 Missing ⚠️
plugins/async-node/core/src/index.ts 80.00% 28 Missing ⚠️
react/player/src/player.tsx 71.59% 24 Missing and 1 partial ⚠️
...e-assets/core/src/plugins/error-recovery-plugin.ts 32.25% 21 Missing ⚠️
core/player/src/view/resolver/index.ts 71.42% 18 Missing ⚠️
core/player/src/view/resolver/ResolverError.ts 40.00% 9 Missing ⚠️
...e/serialization/serializers/ThrowableSerializer.kt 64.00% 6 Missing and 3 partials ⚠️
core/player/src/controllers/view/controller.ts 56.25% 7 Missing ⚠️
.../com/intuit/playerui/core/error/ErrorController.kt 66.66% 2 Missing and 5 partials ⚠️
... and 7 more
Additional details and impacted files
@@                 Coverage Diff                  @@
##           error-controller     #802      +/-   ##
====================================================
- Coverage             86.02%   85.55%   -0.47%     
====================================================
  Files                   513      522       +9     
  Lines                 23566    24094     +528     
  Branches               2703     2787      +84     
====================================================
+ Hits                  20272    20614     +342     
- Misses                 2956     3138     +182     
- Partials                338      342       +4     

☔ View full report in Codecov by Sentry.
📢 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.

@codecov
Copy link

codecov bot commented Feb 13, 2026

Bundle Report

Changes will increase total bundle size by 121.51kB (2.1%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
plugins/beacon/core 422.95kB 5.48kB (1.31%) ⬆️
plugins/check-path/core 441.35kB 5.48kB (1.26%) ⬆️
plugins/stage-revert-data/core 404.97kB 5.48kB (1.37%) ⬆️
react/subscribe 14.11kB 2.48kB (21.27%) ⬆️
plugins/reference-assets/core 502.2kB 23.5kB (4.91%) ⬆️
react/player 83.38kB 10.03kB (13.67%) ⬆️
plugins/common-expressions/core 427.2kB 5.48kB (1.3%) ⬆️
plugins/markdown/core 682.03kB 5.49kB (0.81%) ⬆️
plugins/common-types/core 501.53kB 5.48kB (1.11%) ⬆️
plugins/metrics/core 459.51kB 5.48kB (1.21%) ⬆️
plugins/async-node/core 500.33kB 26.81kB (5.66%) ⬆️
core/player 1.02MB 20.3kB (2.03%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: plugins/check-path/core

Assets Changed:

Asset Name Size Change Total Size Change (%)
CheckPathPlugin.native.js 5.48kB 411.86kB 1.35%
view changes for bundle: react/subscribe

Assets Changed:

Asset Name Size Change Total Size Change (%)
cjs/index.cjs 883 bytes 5.89kB 17.65% ⚠️
index.legacy-esm.js 796 bytes 4.11kB 24.0% ⚠️
index.mjs 796 bytes 4.11kB 24.0% ⚠️
view changes for bundle: plugins/metrics/core

Assets Changed:

Asset Name Size Change Total Size Change (%)
MetricsPlugin.native.js 5.48kB 427.16kB 1.3%
view changes for bundle: plugins/common-expressions/core

Assets Changed:

Asset Name Size Change Total Size Change (%)
CommonExpressionsPlugin.native.js 5.48kB 405.96kB 1.37%
view changes for bundle: plugins/stage-revert-data/core

Assets Changed:

Asset Name Size Change Total Size Change (%)
StageRevertDataPlugin.native.js 5.48kB 398.14kB 1.4%
view changes for bundle: plugins/reference-assets/core

Assets Changed:

Asset Name Size Change Total Size Change (%)
ReferenceAssetsPlugin.native.js 16.96kB 465.76kB 3.78%
cjs/index.cjs 2.19kB 13.38kB 19.58% ⚠️
index.legacy-esm.js 2.18kB 11.53kB 23.27% ⚠️
index.mjs 2.18kB 11.53kB 23.27% ⚠️
view changes for bundle: plugins/beacon/core

Assets Changed:

Asset Name Size Change Total Size Change (%)
BeaconPlugin.native.js 5.48kB 408.48kB 1.36%
view changes for bundle: plugins/markdown/core

Assets Changed:

Asset Name Size Change Total Size Change (%)
MarkdownPlugin.native.js 5.49kB 656.88kB 0.84%
view changes for bundle: core/player

Assets Changed:

Asset Name Size Change Total Size Change (%)
Player.native.js 6.23kB 424.91kB 1.49%
cjs/index.cjs 4.72kB 202.58kB 2.38%
index.legacy-esm.js 4.68kB 195.94kB 2.45%
index.mjs 4.68kB 195.94kB 2.45%
view changes for bundle: plugins/common-types/core

Assets Changed:

Asset Name Size Change Total Size Change (%)
CommonTypesPlugin.native.js 5.48kB 430.36kB 1.29%
view changes for bundle: plugins/async-node/core

Assets Changed:

Asset Name Size Change Total Size Change (%)
AsyncNodePlugin.native.js 13.45kB 439.12kB 3.16%
cjs/index.cjs 4.52kB 21.92kB 25.95% ⚠️
index.legacy-esm.js 4.42kB 19.64kB 29.06% ⚠️
index.mjs 4.42kB 19.64kB 29.06% ⚠️
view changes for bundle: react/player

Assets Changed:

Asset Name Size Change Total Size Change (%)
cjs/index.cjs 3.53kB 30.23kB 13.2% ⚠️
index.legacy-esm.js 3.25kB 26.57kB 13.94% ⚠️
index.mjs 3.25kB 26.57kB 13.94% ⚠️

@tmarmer
Copy link
Contributor Author

tmarmer commented Feb 13, 2026

/canary

intuit-svc added a commit to player-ui/player-ui.github.io that referenced this pull request Feb 13, 2026
@intuit-svc
Copy link
Contributor

Build Preview

Your PR was deployed by CircleCI #31569 on Fri, 13 Feb 2026 14:55:49 GMT with this version:

0.15.1--canary.802.31569

📖 Docs (View site)

@tmarmer tmarmer requested a review from a team as a code owner February 24, 2026 21:45
@tmarmer tmarmer marked this pull request as draft March 10, 2026 18:57
Comment on lines +45 to +60
/** Returns a function to be used as the `replacer` for JSON.stringify that tracks and ignores circular references. */
const makeJsonStringifyReplacer = (): ReplacerFunction => {
const cache = new Set();
return (_: string, value: any) => {
if (typeof value === "object" && value !== null) {
if (cache.has(value)) {
// Circular reference found, discard key
return "[CIRCULAR]";
}
// Store value in our collection
cache.add(value);
}
return value;
};
};

Copy link
Member

Choose a reason for hiding this comment

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

Why do we need to do this? Shouldn't internal Player state be serializable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The issue here stems from the ResolverError adding in the failed node in its metadata. We need a better solution but this was put in place to allow the metadata to get logged without throwing errors since the node itself has circular references.

If we don't want the node logged or set in the dataController as part of captureError we either need to not include the metadata in logs/data or we need another mechanism to identify resolver nodes with to avoid sending the whole object to keep its reference.

Copy link
Member

Choose a reason for hiding this comment

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

So this is so that the error includes the originating AST node, gotcha. I think a reference should be able to be stored fine in the Data Controller without serialization right? For logging can we just reference the asset ID?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the error controller was aware of the metadata being logged we could, but because any error with any metadata could pass through it, we can't identify when we're getting something like a Node or a simpler object. That and sometimes the originating Node being logged might be an async node or something else that generated the asset.

Comment on lines +204 to +211
export const ResolverStages = {
ResolveOptions: "resolve-options",
SkipResolve: "skip-resolve",
BeforeResolve: "before-resolve",
Resolve: "resolve",
AfterResolve: "after-resolve",
AfterNodeUpdate: "after-node-update",
} as const;
Copy link
Member

Choose a reason for hiding this comment

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

Is there a way we can automate this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Couldn't find a way to automatically form this object, but simplified the types here to use an enum. If you have any ideas here I'm open to try something else

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