Skip to content

fix: prevent My Account 500s from empty session profile and missing CMS content - #3417

Merged
lemagnetic merged 5 commits into
devfrom
fix/my-account-profile-data
Jul 28, 2026
Merged

fix: prevent My Account 500s from empty session profile and missing CMS content#3417
lemagnetic merged 5 commits into
devfrom
fix/my-account-profile-data

Conversation

@lemagnetic

@lemagnetic lemagnetic commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What's the purpose of this pull request?

My Account routes (pvt/account/*, pvt/organization-account/*) can return HTTP 500 store-wide. Two independent triggers cause it: (1) a representative session whose VTEX profile namespace comes back empty, making accountProfile call getContractById('') and throw a 400; (2) a My Account content type without a published CMS document, which resolves to undefined and is dereferenced. The /pvt/account/404 fallback runs the same queries, so it also 500s and there is no graceful degradation.

How it works?

  • @faststore/api accountProfile: aligns with validateSession — resolves the contract id via resolveActiveContractIdFromSession with a jwt.customerId fallback, only calls getContractById when an id exists, and wraps it in try/catch. Name/email/id fall back to the shopper/authentication namespaces and the JWT.
  • @faststore/core My Account SSR: defaults CMS content to empty sections when no document is published, and stops dereferencing account.data.accountProfile.name / page without guarding null/undefined (including the /404 fallback page).

How to test it?

  • Sign in to a B2B storefront as a representative user and navigate to any My Account page (e.g. /pvt/account/profile); it should render instead of 500, even when the session profile namespace is empty.
  • Unpublish a My Account content type (e.g. myAccountProfile) in the CMS and open the page; it should render default sections instead of 500.
  • cd packages/api && pnpm test:unit (new accountProfile cases covering empty/all-null sessions).

Starters Deploy Preview

N/A

References

Summary by CodeRabbit

  • Bug Fixes
    • Improved representative account profile resolution by deriving name/email/id from VTEX session data when profile fields are missing, and making contract lookup conditional and more fault-tolerant.
    • Hardened My Account server-side props and the account 404 page, plus CMS page content loading, with safe fallbacks when data is missing.
  • Tests
    • Added unit coverage for representative account profile resolution, including missing contract IDs, lookup failures, and session profile precedence.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f5885c55-afda-4852-ae20-dff7ebd5e76e

📥 Commits

Reviewing files that changed from the base of the PR and between 3645750 and d56dd4e.

📒 Files selected for processing (2)
  • packages/api/src/platforms/vtex/resolvers/query.ts
  • packages/core/src/pages/pvt/account/404.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/core/src/pages/pvt/account/404.tsx
  • packages/api/src/platforms/vtex/resolvers/query.ts

Walkthrough

Representative account profiles now use session and JWT fallbacks with guarded contract fetching. Account server-side props and CMS content loading now tolerate missing profile or page data through null-safe defaults.

Changes

Account profile and page resilience

Layer / File(s) Summary
Representative profile resolution
packages/api/src/platforms/vtex/resolvers/query.ts, packages/api/test/unit/platforms/vtex/resolvers/query.test.ts
Representative profiles derive contract identifiers from session or JWT data, conditionally fetch contracts, handle fetch failures, and resolve name, email, and id from available session values.
Account page and CMS fallbacks
packages/core/src/server/cms/fetchMyAccountPageContent.ts, packages/core/src/experimental/myAccountServerSideProps.ts, packages/core/src/pages/pvt/account/404.tsx
CMS content and account page props now default missing content, sections, and account names to empty values.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant JWT
  participant Session
  participant QueryAccountProfile
  participant MasterData
  JWT->>QueryAccountProfile: provide representative identity and customerId
  QueryAccountProfile->>Session: read shopper, authentication, and active contract
  QueryAccountProfile->>MasterData: fetch contract when contractId exists
  MasterData-->>QueryAccountProfile: return contract or error
  QueryAccountProfile-->>JWT: return resolved account profile
Loading

Possibly related PRs

  • vtex/faststore#3390: Updates the same VTEX representative account profile contract and session resolution behavior.

Suggested reviewers: lucasfp13, eduardoformiga, hellofanny

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main fix for My Account 500s caused by empty session profiles and missing CMS content.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/my-account-profile-data

Comment @coderabbitai help to get the list of available commands.

@codesandbox-ci

codesandbox-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

@lemagnetic lemagnetic added the bug Something isn't working label Jul 28, 2026
@lemagnetic lemagnetic changed the title Fix/my account profile data fix: prevent My Account 500s from empty session profile and missing CMS content Jul 28, 2026
@lemagnetic
lemagnetic marked this pull request as ready for review July 28, 2026 01:32
@lemagnetic
lemagnetic requested a review from a team as a code owner July 28, 2026 01:32
@lemagnetic
lemagnetic requested review from eduardoformiga and lucasfp13 and removed request for a team July 28, 2026 01:32
@lemagnetic
lemagnetic force-pushed the fix/my-account-profile-data branch from 6f7396a to 9ca36d6 Compare July 28, 2026 01:36

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/api/src/platforms/vtex/resolvers/query.ts`:
- Around line 752-779: Normalize the JWT customer ID before using it in the
contract lookup and returned profile ID: trim jwt?.customerId and treat an empty
result as unavailable. Update the contractId fallback in the resolver flow
around resolveActiveContractIdFromSession and ensure the returned id fallback
uses the same normalized value, matching the availableContracts behavior.

In `@packages/core/src/server/cms/fetchMyAccountPageContent.ts`:
- Around line 34-35: The fallbacks in fetchMyAccountPageContent and the account
404 page claim the full PageContentType while omitting required settings.seo
fields. Update both packages/core/src/server/cms/fetchMyAccountPageContent.ts
lines 34-35 and packages/core/src/pages/pvt/account/404.tsx line 150 to provide
the complete default SEO object, or narrow both call sites to the sections-only
type.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4eb4f967-6eba-4837-9f93-f39fe9908b9f

📥 Commits

Reviewing files that changed from the base of the PR and between eb85986 and 6f7396a.

📒 Files selected for processing (5)
  • packages/api/src/platforms/vtex/resolvers/query.ts
  • packages/api/test/unit/platforms/vtex/resolvers/query.test.ts
  • packages/core/src/experimental/myAccountServerSideProps.ts
  • packages/core/src/pages/pvt/account/404.tsx
  • packages/core/src/server/cms/fetchMyAccountPageContent.ts

Comment thread packages/api/src/platforms/vtex/resolvers/query.ts
Comment on lines +34 to +35
return { sections: [], settings: {} } as PageContentType
})) ?? ({ sections: [], settings: {} } as PageContentType)

@coderabbitai coderabbitai Bot Jul 28, 2026

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== fetchMyAccountPageContent ==\n'
sed -n '1,140p' packages/core/src/server/cms/fetchMyAccountPageContent.ts

printf '\n== account 404 page excerpt ==\n'
sed -n '120,190p' packages/core/src/pages/pvt/account/404.tsx

printf '\n== PageContentType definition search ==\n'
rg -n "type PageContentType|interface PageContentType|PageContentType" packages/core/src -g '!**/node_modules/**'

Repository: vtex/faststore

Length of output: 6961


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== PageContentType definition ==\n'
sed -n '1,140p' packages/core/src/server/cms/index.ts

printf '\n== myAccountDefaultSections ==\n'
sed -n '1,220p' packages/core/src/server/cms/myAccountDefaultSections.ts

printf '\n== getPage / PageContentType handling ==\n'
sed -n '150,230p' packages/core/src/server/content/service.ts

printf '\n== pvt account 404 data flow ==\n'
sed -n '1,170p' packages/core/src/pages/pvt/account/404.tsx

Repository: vtex/faststore

Length of output: 12992


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== fetchMyAccountPageContent usages ==\n'
rg -n "fetchMyAccountPageContent\(" packages/core/src

printf '\n== PageContentType field access in the account area ==\n'
rg -n "page\.settings|settings\.seo|globalSettings|PageProvider context=\{\{ globalSettings \}\}" packages/core/src/pages/pvt/account packages/core/src/components -g '!**/node_modules/**'

printf '\n== direct fallback patterns around PageContentType ==\n'
rg -n "as unknown as PageContentType|as PageContentType" packages/core/src/pages/pvt/account packages/core/src/server/cms packages/core/src/server/content/service.ts

Repository: vtex/faststore

Length of output: 6369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in \
  packages/core/src/pages/pvt/account/profile.tsx \
  packages/core/src/pages/pvt/account/security.tsx \
  packages/core/src/pages/pvt/account/403.tsx \
  packages/core/src/pages/pvt/account/orders/index.tsx \
  packages/core/src/pages/pvt/account/user-details.tsx
do
  printf '\n== %s ==\n' "$f"
  sed -n '1,220p' "$f" | rg -n "fetchMyAccountPageContent|page\.settings|settings\.seo|page:\s*page|Props|function Page|const Page"
done

Repository: vtex/faststore

Length of output: 2819


Return a complete fallback shape

Both fallbacks are cast to PageContentType while omitting the required settings.seo fields.

  • packages/core/src/server/cms/fetchMyAccountPageContent.ts#L34-L35
  • packages/core/src/pages/pvt/account/404.tsx#L150-L150

Either provide the full default SEO object or narrow these call sites to the sections-only shape.

📍 Affects 2 files
  • packages/core/src/server/cms/fetchMyAccountPageContent.ts#L34-L35 (this comment)
  • packages/core/src/pages/pvt/account/404.tsx#L150-L150
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/server/cms/fetchMyAccountPageContent.ts` around lines 34 -
35, The fallbacks in fetchMyAccountPageContent and the account 404 page claim
the full PageContentType while omitting required settings.seo fields. Update
both packages/core/src/server/cms/fetchMyAccountPageContent.ts lines 34-35 and
packages/core/src/pages/pvt/account/404.tsx line 150 to provide the complete
default SEO object, or narrow both call sites to the sections-only type.

Source: Path instructions

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it important in this situation to type the settings.seo fields as well?

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.

@eduardoformiga, yes—if this function continues to return a PageContentType, the fallback should include the required settings.seo shape.

The current as PageContentType only suppresses TypeScript’s check; it does not make settings.seo exist at runtime. That can cause another SSR error if an account page or shared layout reads SEO settings later.

If the My Account fallback is genuinely intended to support only sections, the alternative is to narrow the function’s return type and ensure all callers accept that reduced shape. Given it spreads and returns CMS page content, a complete typed fallback (ideally a shared default) is the safer, clearer option. 🐇🔎

@pkg-pr-new

pkg-pr-new Bot commented Jul 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

@faststore/api

npm i https://pkg.pr.new/vtex/faststore/@faststore/api@d56dd4e

@faststore/cli

npm i https://pkg.pr.new/vtex/faststore/@faststore/cli@d56dd4e

@faststore/components

npm i https://pkg.pr.new/vtex/faststore/@faststore/components@d56dd4e

@faststore/core

npm i https://pkg.pr.new/vtex/faststore/@faststore/core@d56dd4e

@faststore/diagnostics

npm i https://pkg.pr.new/vtex/faststore/@faststore/diagnostics@d56dd4e

@faststore/lighthouse

npm i https://pkg.pr.new/vtex/faststore/@faststore/lighthouse@d56dd4e

@faststore/sdk

npm i https://pkg.pr.new/vtex/faststore/@faststore/sdk@d56dd4e

@faststore/ui

npm i https://pkg.pr.new/vtex/faststore/@faststore/ui@d56dd4e

commit: d56dd4e

@renatomaurovtex renatomaurovtex left a comment

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.

Reviewed the undrafted version. The fix addresses both 500 triggers correctly, and I verified the details on the head branch:

  • The contract-id resolution now mirrors the validateSession path (resolveActiveContractIdFromSessionjwt.customerId fallback), so the two resolvers can't drift apart on which contract is "active". Guarding getContractById behind a non-empty id kills the original getContractById('') 400.
  • fetchMyAccountPageContent: the added ?? fallback is the actual fix for the unpublished-document case — getSingleContent resolves undefined there, so the pre-existing .catch fallback never fired. Good catch.
  • The 4 new unit tests cover the right failure modes (all-null session, no contract id anywhere, getContractById rejecting, populated-profile precedence).
  • Everything fails open into a degraded render instead of a 500, consistent with the existing CMS fallback philosophy. 👍

packages/api/test/unit/platforms/vtex/resolvers/query.test.ts

🟡 ⚡ [ci] The red FastStore check is a Biome format error in this file — trailing blank line at the end (line 690). pnpm lint:fix resolves it; needs to land for CI to go green.

packages/api/src/platforms/vtex/resolvers/query.ts

🟡 ⚡ [observability] The catch swallows the error it caught

err is bound but never logged, so when contract lookup starts failing in production the log tells you that it failed but not why (400 vs 429 vs timeout). There's also a stray trailing \n in the template string.

-        } catch (err) {
-          console.error(
-            `Error while getting contract data for contract ID (${contractId}).\n`
-          )
-        }
+        } catch (err) {
+          console.error(
+            `Error while getting contract data for contract ID (${contractId}).`,
+            err
+          )
+        }

💬 [data] Do we want the raw contract id (Master Data document id) in server logs? Probably fine since these logs are store-scoped, but flagging since it's customer master data — if there's any doubt, log only its presence/length.

packages/core/src/pages/pvt/account/404.tsx

🟡 [types] Fallback shape doesn't match the type it's cast to (CodeRabbit flagged this too)

{ sections: [] } as unknown as PageContentType omits settings. Today the component only destructures page: { sections }, so there's no runtime break — but the as unknown as double-cast means the compiler will stay silent if someone later reads page.settings.seo on the fallback path. Align it with the fallback fetchMyAccountPageContent already uses (ideally extract one shared EMPTY_PAGE_CONTENT constant instead of the three inline copies this PR now has):

-      page: page ?? ({ sections: [] } as unknown as PageContentType),
+      page: page ?? ({ sections: [], settings: {} } as PageContentType),

Verdict: Approved with comments

Blocking (🔴/🟠):

  • None — but the 🟡 Biome format fix in query.test.ts is required anyway for the FastStore check to pass.

Non-blocking (🟡/💬):

  • Include the caught error in the getContractById failure log.
  • Complete the PageContentType fallback shape in 404.tsx (drop the as unknown as).
  • 💬 Confirm logging the raw contract id is acceptable.

Checks to confirm before merge: pnpm lint (currently red) · build · cd packages/api && pnpm test:unit · pnpm size (api/core touched)

@eduardoformiga eduardoformiga left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM! But would be good to double-check the failing GH checks.


const contractId =
resolveActiveContractIdFromSession(sessionData) ||
jwt?.customerId?.trim() ||

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nice!

try {
contract = await commerce.masterData.getContractById({ contractId })
} catch (err) {
console.error(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

now sure if we should console an error or just throw as error here

Comment on lines +34 to +35
return { sections: [], settings: {} } as PageContentType
})) ?? ({ sections: [], settings: {} } as PageContentType)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it important in this situation to type the settings.seo fields as well?

@sonar-workflows

Copy link
Copy Markdown

@lemagnetic
lemagnetic merged commit 2c2a18f into dev Jul 28, 2026
13 of 14 checks passed
@lemagnetic
lemagnetic deleted the fix/my-account-profile-data branch July 28, 2026 20:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants