Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
🚅 Deployed to the echo-pr-698 environment in echo
|
| const membership = await utils.apps.app.memberships.get | ||
| .fetch({ appId }) | ||
| .catch(() => null); | ||
|
|
||
| // Only update if user has no referrer yet | ||
| if (membership?.referrerId === null) { | ||
| await updateReferrer({ | ||
| appId, | ||
| referrerId: referralCodeData.id, | ||
| }).catch(() => { | ||
| // Silently fail - user might already have a referrer from a race condition | ||
| }); | ||
| } | ||
|
|
||
| // Create membership if it doesn't exist | ||
| if (!membership) { | ||
| await createMembership({ | ||
| appId, | ||
| referrerId: referralCodeData.id, | ||
| }).catch(() => { | ||
| // Silently fail - membership might have been created in the meantime | ||
| }); | ||
| } |
There was a problem hiding this comment.
The component conflates API fetch errors with "membership doesn't exist", potentially causing duplicate membership creation attempts if the membership fetch fails.
View Details
📝 Patch Details
diff --git a/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/referral-handler.tsx b/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/referral-handler.tsx
index ed0746c0..0e4d2ae9 100644
--- a/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/referral-handler.tsx
+++ b/packages/app/control/src/app/(app)/app/[id]/(overview)/_components/referral-handler.tsx
@@ -23,18 +23,32 @@ export const ReferralHandler: React.FC<Props> = ({ appId }) => {
if (!referralCode || processed) return;
const processReferralCode = async () => {
- const referralCodeData = await utils.apps.app.referralCode.get.byCode
- .fetch(referralCode)
- .catch(() => null);
+ let referralCodeData;
+ try {
+ referralCodeData = await utils.apps.app.referralCode.get.byCode.fetch(
+ referralCode
+ );
+ } catch (error) {
+ console.error('Failed to fetch referral code:', error);
+ setProcessed(true);
+ return;
+ }
if (!referralCodeData) {
setProcessed(true);
return;
}
- const membership = await utils.apps.app.memberships.get
- .fetch({ appId })
- .catch(() => null);
+ let membership;
+ try {
+ membership = await utils.apps.app.memberships.get.fetch({ appId });
+ } catch (error) {
+ // Failed to fetch membership - cannot distinguish between "doesn't exist" and "fetch failed"
+ // Avoid attempting to create membership when we don't know the actual state
+ console.error('Failed to fetch membership:', error);
+ setProcessed(true);
+ return;
+ }
// Only update if user has no referrer yet
if (membership?.referrerId === null) {
@@ -46,7 +60,7 @@ export const ReferralHandler: React.FC<Props> = ({ appId }) => {
});
}
- // Create membership if it doesn't exist
+ // Create membership if it doesn't exist (and fetch succeeded confirming non-existence)
if (!membership) {
await createMembership({
appId,
Analysis
ReferralHandler conflates API fetch errors with membership non-existence, risking duplicate membership creation
What fails: In ReferralHandler.processReferralCode(), when memberships.get.fetch() fails with a network or server error (lines 35-37), it's silently caught and converted to null via .catch(() => null). This creates ambiguity: the code cannot distinguish between "membership doesn't exist" (legitimate case where create is safe) and "membership fetch failed" (error case where state is unknown). Result: if the fetch fails when a membership already exists, the code proceeds to attempt creating a duplicate membership at line 50.
How to reproduce:
- User with an existing membership visits the app with a referral code query parameter
- Network failure or server error occurs during the
memberships.get.fetch({ appId })call - The
.catch(() => null)suppresses the error and setsmembershiptonull - Code reaches line 50 where
if (!membership)evaluates to true - Code attempts
createMembership()even though the membership likely exists
Result: Multiple membership creation attempts triggered by transient API errors, leading to:
- Unexpected API responses from duplicate creation attempts (membership already exists)
- Potential for data inconsistency if error handling in create/update operations relies on specific error codes
- User confusion or silent failures
Expected behavior: Errors during membership fetch should be handled distinctly from the legitimate "no membership exists" case. If we cannot determine membership state due to a fetch error, we should not attempt creation.
Fix implemented: Replaced .catch(() => null) pattern with explicit try-catch blocks that:
- Distinguish fetch errors from successful null responses
- Log errors for observability
- Return early when fetch fails (rather than attempting creation with unknown state)
- Only proceed to create membership when fetch succeeds and returns null (confirming non-existence)
This ensures duplicate creation attempts only occur due to documented race conditions (lines 54, 65 comments), not API failures.
No description provided.