Skip to content

fix(account-tree-controller/backup-and-sync): do not send extra metadata properties#8300

Merged
ccharly merged 4 commits intomainfrom
cc/fix/backup-and-sync-filter-out-extra-properties
Mar 26, 2026
Merged

fix(account-tree-controller/backup-and-sync): do not send extra metadata properties#8300
ccharly merged 4 commits intomainfrom
cc/fix/backup-and-sync-filter-out-extra-properties

Conversation

@ccharly
Copy link
Copy Markdown
Contributor

@ccharly ccharly commented Mar 25, 2026

Explanation

Since the introduction of lastSelected metadata (which should not be synced), backup & sync was not accepting the data coming back from user-storage. Mostly because the payload was like this:

"{\"name\":{\"value\":\"Account 1\",\"lastUpdatedAt\":0},\"pinned\":{\"value\":false,\"lastUpdatedAt\":0},\"hidden\":{\"value\":false,\"lastUpdatedAt\":0},\"lastSelected\":0,\"groupIndex\":0}"
"{\"name\":{\"value\":\"Account 2\",\"lastUpdatedAt\":0},\"pinned\":{\"value\":false,\"lastUpdatedAt\":0},\"hidden\":{\"value\":false,\"lastUpdatedAt\":0},\"lastSelected\":0,\"groupIndex\":1}"

The extra lastSelected shouldn't have been part of that payload. This was making the superstruct validation to fail when getting those data back, thus, preventing account syncing to finally restore accounts.

We now mask the extra properties that are not part of backup & sync schema.

References

N/A

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Touches backup/sync serialization and error handling; incorrect masking/fallback could cause some group metadata (e.g., name/pin/hide) not to persist or to be dropped in edge cases.

Overview
Fixes backup & sync group serialization to drop extra/invalid metadata fields before writing to user storage by masking group metadata against UserStorageSyncedWalletGroupSchema (preventing fields like lastSelected from breaking later reads/validation), with a safe fallback to minimal { groupIndex } plus logging.

Standardizes unknown-error stringification via a new toErrorMessage utility and applies it across backup/sync error paths, alongside new/updated unit tests and a changelog entry documenting the user-storage filtering fix.

Written by Cursor Bugbot for commit cfa55f2. This will update automatically on new commits. Configure here.

@ccharly
Copy link
Copy Markdown
Contributor Author

ccharly commented Mar 25, 2026

@metamaskbot publish-previe

@ccharly
Copy link
Copy Markdown
Contributor Author

ccharly commented Mar 25, 2026

@metamaskbot publish-preview

@github-actions
Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@5.0.1-preview-afe010990
@metamask-previews/accounts-controller@37.0.0-preview-afe010990
@metamask-previews/address-book-controller@7.1.0-preview-afe010990
@metamask-previews/ai-controllers@0.6.0-preview-afe010990
@metamask-previews/analytics-controller@1.0.0-preview-afe010990
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-afe010990
@metamask-previews/announcement-controller@8.0.0-preview-afe010990
@metamask-previews/app-metadata-controller@2.0.0-preview-afe010990
@metamask-previews/approval-controller@9.0.0-preview-afe010990
@metamask-previews/assets-controller@3.1.1-preview-afe010990
@metamask-previews/assets-controllers@101.0.1-preview-afe010990
@metamask-previews/base-controller@9.0.0-preview-afe010990
@metamask-previews/base-data-service@0.1.0-preview-afe010990
@metamask-previews/bridge-controller@69.2.1-preview-afe010990
@metamask-previews/bridge-status-controller@70.0.1-preview-afe010990
@metamask-previews/build-utils@3.0.4-preview-afe010990
@metamask-previews/chain-agnostic-permission@1.5.0-preview-afe010990
@metamask-previews/claims-controller@0.4.3-preview-afe010990
@metamask-previews/client-controller@1.0.0-preview-afe010990
@metamask-previews/compliance-controller@1.0.1-preview-afe010990
@metamask-previews/composable-controller@12.0.0-preview-afe010990
@metamask-previews/config-registry-controller@0.1.1-preview-afe010990
@metamask-previews/connectivity-controller@0.1.0-preview-afe010990
@metamask-previews/controller-utils@11.19.0-preview-afe010990
@metamask-previews/core-backend@6.2.0-preview-afe010990
@metamask-previews/delegation-controller@2.0.2-preview-afe010990
@metamask-previews/earn-controller@11.1.2-preview-afe010990
@metamask-previews/eip-5792-middleware@3.0.1-preview-afe010990
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.0-preview-afe010990
@metamask-previews/eip1193-permission-middleware@1.0.3-preview-afe010990
@metamask-previews/ens-controller@19.1.0-preview-afe010990
@metamask-previews/error-reporting-service@3.0.1-preview-afe010990
@metamask-previews/eth-block-tracker@15.0.1-preview-afe010990
@metamask-previews/eth-json-rpc-middleware@23.1.0-preview-afe010990
@metamask-previews/eth-json-rpc-provider@6.0.0-preview-afe010990
@metamask-previews/foundryup@1.0.1-preview-afe010990
@metamask-previews/gas-fee-controller@26.1.0-preview-afe010990
@metamask-previews/gator-permissions-controller@2.1.1-preview-afe010990
@metamask-previews/geolocation-controller@0.1.1-preview-afe010990
@metamask-previews/json-rpc-engine@10.2.3-preview-afe010990
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-afe010990
@metamask-previews/keyring-controller@25.1.0-preview-afe010990
@metamask-previews/logging-controller@8.0.0-preview-afe010990
@metamask-previews/message-manager@14.1.0-preview-afe010990
@metamask-previews/messenger@0.3.0-preview-afe010990
@metamask-previews/multichain-account-service@7.1.0-preview-afe010990
@metamask-previews/multichain-api-middleware@2.0.0-preview-afe010990
@metamask-previews/multichain-network-controller@3.0.5-preview-afe010990
@metamask-previews/multichain-transactions-controller@7.0.2-preview-afe010990
@metamask-previews/name-controller@9.1.0-preview-afe010990
@metamask-previews/network-controller@30.0.0-preview-afe010990
@metamask-previews/network-enablement-controller@5.0.0-preview-afe010990
@metamask-previews/notification-services-controller@23.0.0-preview-afe010990
@metamask-previews/permission-controller@12.2.1-preview-afe010990
@metamask-previews/permission-log-controller@5.0.0-preview-afe010990
@metamask-previews/perps-controller@1.3.0-preview-afe010990
@metamask-previews/phishing-controller@17.0.0-preview-afe010990
@metamask-previews/polling-controller@16.0.3-preview-afe010990
@metamask-previews/preferences-controller@23.0.0-preview-afe010990
@metamask-previews/profile-metrics-controller@3.1.1-preview-afe010990
@metamask-previews/profile-sync-controller@28.0.0-preview-afe010990
@metamask-previews/ramps-controller@12.0.1-preview-afe010990
@metamask-previews/rate-limit-controller@7.0.0-preview-afe010990
@metamask-previews/react-data-query@0.1.0-preview-afe010990
@metamask-previews/remote-feature-flag-controller@4.1.0-preview-afe010990
@metamask-previews/sample-controllers@4.0.3-preview-afe010990
@metamask-previews/seedless-onboarding-controller@9.0.0-preview-afe010990
@metamask-previews/selected-network-controller@26.0.3-preview-afe010990
@metamask-previews/shield-controller@5.0.2-preview-afe010990
@metamask-previews/signature-controller@39.1.0-preview-afe010990
@metamask-previews/storage-service@1.0.0-preview-afe010990
@metamask-previews/subscription-controller@6.0.2-preview-afe010990
@metamask-previews/transaction-controller@63.1.0-preview-afe010990
@metamask-previews/transaction-pay-controller@18.1.0-preview-afe010990
@metamask-previews/user-operation-controller@41.1.0-preview-afe010990

@ccharly ccharly marked this pull request as ready for review March 25, 2026 16:12
@ccharly ccharly requested review from a team as code owners March 25, 2026 16:12
* @param error - The caught error value.
* @returns The error message string.
*/
export function toErrorMessage(error: unknown): string {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added this mostly because it was hard to get coverage when re-using this pattern in the fix I made in formatGroupForUserStorageUsage.

Otherwise I would have to mock superstruct.mask just to throw non-Error errors, and that felt like a bit of an anti-pattern.

Delegating this coverage to this helper makes the code easier and keep the test focused on the new behavior fix (IMO)!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

(I used a similar pattern in the service too, toErrorMessage makes things a bit more readable IMO)

Copy link
Copy Markdown
Contributor

@mathieuartu mathieuartu left a comment

Choose a reason for hiding this comment

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

LGTM, just one curiosity question before approving 😛

);

// If anything goes wrong with this group, we use blank metadata for it.
return { groupIndex };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's the rationale behind keeping groupIndex here? Does this help the account tree to be built properly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I just kept the semantic of the function (it couldn't fail/throw before we started introducing the mask call), so in case an error happens, I don't want to make it throw either.

IDK if we can do better than that actually 🤔 the way I see this is that, we need at least the groupIndex, everything else (for now) is optional, so worst case scenario, we omit all metadata and go with the groupIndex.

Would you prefer to throw instead, so this could bubble up and potentially make B&S fail entirely (I haven't analyzed the call sites TBH 🙊)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks! No, I actually didn't think that far ahead, I just wanted to understand your intent. I think your logic is sound :)

Copy link
Copy Markdown
Contributor

@mathieuartu mathieuartu left a comment

Choose a reason for hiding this comment

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

LGTM, did not test

@ccharly ccharly added this pull request to the merge queue Mar 26, 2026
Merged via the queue into main with commit fb601af Mar 26, 2026
326 checks passed
@ccharly ccharly deleted the cc/fix/backup-and-sync-filter-out-extra-properties branch March 26, 2026 12:56
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