Skip to content

Enhance error handling and logging in Emailable response validation#1292

Open
mantrakp04 wants to merge 6 commits intodevfrom
chore/signup-codes-n-emailable
Open

Enhance error handling and logging in Emailable response validation#1292
mantrakp04 wants to merge 6 commits intodevfrom
chore/signup-codes-n-emailable

Conversation

@mantrakp04
Copy link
Copy Markdown
Collaborator

@mantrakp04 mantrakp04 commented Mar 26, 2026

  • Updated validateVerifyResponse to capture errors for invalid or unexpected responses.
  • Improved handling of malformed responses in checkEmailWithEmailable, ensuring a consistent return structure.
  • Refactored getDerivedSignUpCountryCode to log errors for non-ISO country codes.
  • Simplified country code determination logic in createOrUpgradeAnonymousUserWithRules.

Summary by CodeRabbit

  • New Features

    • Replaced country code dropdown selection with a direct text input field for simpler data entry.
    • Updated country code validation to accept any 2-letter code format, improving flexibility.
  • Bug Fixes

    • Refined country code normalization logic across sign-up rules and user profile pages for consistency.
  • Documentation

    • Clarified country code field messaging from "ISO code" to "2-letter country code" terminology for better user guidance.

- Updated `validateVerifyResponse` to capture errors for invalid or unexpected responses.
- Improved handling of malformed responses in `checkEmailWithEmailable`, ensuring a consistent return structure.
- Refactored `getDerivedSignUpCountryCode` to log errors for non-ISO country codes.
- Simplified country code determination logic in `createOrUpgradeAnonymousUserWithRules`.
Copilot AI review requested due to automatic review settings March 26, 2026 16:49
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment Mar 26, 2026 8:42pm
stack-backend Ready Ready Preview, Comment Mar 26, 2026 8:42pm
stack-dashboard Ready Ready Preview, Comment Mar 26, 2026 8:42pm
stack-demo Ready Ready Preview, Comment Mar 26, 2026 8:42pm
stack-docs Ready Ready Preview, Comment Mar 26, 2026 8:42pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

Refactored country code validation from set-based lookup to regex-based validation, replacing a hardcoded ISO country code list with dynamic validation. Updated UI components from dropdown selection to direct text input. Removed public exports of ISO country code constants and updated validation messaging across backend and frontend.

Changes

Cohort / File(s) Summary
Country Code Validation Utilities
packages/stack-shared/src/utils/country-codes.ts, packages/stack-shared/src/schema-fields.ts
Removed exported ISO_3166_ALPHA_2_COUNTRY_CODES, Iso3166Alpha2CountryCode type, and validCountryCodeSet. Changed isValidCountryCode to use regex validation (^[A-Z]{2}$) instead of set membership. Updated error messages from "ISO 3166-1 alpha-2" to "2-letter code" phrasing. Stopped re-exporting removed constants.
Backend Country Code Handling
apps/backend/src/lib/users.tsx, apps/backend/src/lib/end-users.tsx
Swapped validation call from validCountryCodeSet.has() to isValidCountryCode() in user signup logic. Added normalizeCountryCode normalization for browser-derived country codes. Modified countryCode derivation to always compute via getDerivedSignUpCountryCode(). Enhanced test suite with additional country code expectations.
Frontend Country Code Input Component
apps/dashboard/src/components/country-code-select.tsx
Replaced dropdown/popover UI with direct text input. Renamed CountryCodeSelect to CountryCodeInput. Removed dependency on ISO country code dataset. Changed behavior to transform input to uppercase, strip non-letters, limit to 2 characters, and convert empty input to null. Updated placeholder from "Select country code..." to "e.g. US".
Frontend Page Usage
apps/dashboard/src/app/.../**/sign-up-rules/page-client.tsx, apps/dashboard/src/app/.../**/users/[userId]/page-client.tsx
Replaced CountryCodeSelect component with CountryCodeInput in signup rules and user pages. Updated validation error message phrasing from "two-letter ISO code" to "2-letter code".
Frontend Rule Builder
apps/dashboard/src/components/rule-builder/condition-builder.tsx
Replaced CountryCodeSelect with CountryCodeInput for country code list and single field rendering. Removed allowClear={false} prop. Updated helper text to specify "2-letter country codes" instead of "ISO country codes".
CEL Validation Parser
apps/dashboard/src/lib/cel-visual-parser.ts
Modified normalizeConditionValue to pass numeric country code values through unchanged instead of throwing error, allowing downstream CEL generation to handle numeric values.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • N2D4

Poem

🐰 Hop, hop, the codes now dance free
No more set lookups, just regex decree
From dropdowns to input, a simpler way,
Two letters suffice to validate the day!
🌍✨

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title describes error handling and logging enhancements for Emailable response validation, but the changeset primarily focuses on country code validation refactoring (replacing CountryCodeSelect with CountryCodeInput, changing validation logic, removing constants). The title does not reflect this major work. Revise the title to accurately reflect the primary changes, such as 'Refactor country code validation to use 2-letter regex-based validation' or 'Replace CountryCodeSelect with CountryCodeInput and update country code validation', as these represent the bulk of the code changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description mentions four main points, but the actual changeset reveals country code validation refactoring is the dominant work. The description mentions this only as 'Refactored getDerivedSignUpCountryCode to log errors for non-ISO country codes', which understates the scope of changes across multiple files. Expand the description to clearly detail the country code validation refactoring, including removal of the ISO country code list, changes to validation logic, component replacements (CountryCodeSelect → CountryCodeInput), and affected files across the dashboard and backend.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 chore/signup-codes-n-emailable

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/backend/src/lib/emailable.tsx (1)

47-47: Add a comment explaining the any type usage.

As per coding guidelines, the any type should include a comment explaining why it's needed and how errors would still be flagged.

📝 Suggested comment
-    const res: any = await verifyFn();
+    // any: verifyFn returns unknown external API response; validated downstream via validateVerifyResponse
+    const res: any = await verifyFn();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/backend/src/lib/emailable.tsx` at line 47, Add an inline comment
explaining why the local variable "res" is typed as "any" when assigned from
"await verifyFn()", clarifying that verifyFn() returns a dynamic/third-party
shape that cannot be strongly typed here, how runtime/validation still enforces
expected fields (e.g., via explicit runtime checks or schema validation), and
noting how TypeScript errors will still be caught elsewhere; place this comment
directly above the "const res: any = await verifyFn();" declaration and
reference "verifyFn" and any subsequent runtime checks used to validate "res".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/backend/src/lib/emailable.tsx`:
- Around line 51-54: The current retry check in emailable.tsx uses "message" in
res and then calls res.message.includes(...), which can throw if res.message is
not a string; update the condition around the res check (the block referencing
res, wait, delayBaseMs and loop index i) to ensure typeof res.message ===
"string" (or otherwise coerce to a string) before calling .includes, so the
includes call is only executed when res.message is a string and avoids a
TypeError.

---

Nitpick comments:
In `@apps/backend/src/lib/emailable.tsx`:
- Line 47: Add an inline comment explaining why the local variable "res" is
typed as "any" when assigned from "await verifyFn()", clarifying that verifyFn()
returns a dynamic/third-party shape that cannot be strongly typed here, how
runtime/validation still enforces expected fields (e.g., via explicit runtime
checks or schema validation), and noting how TypeScript errors will still be
caught elsewhere; place this comment directly above the "const res: any = await
verifyFn();" declaration and reference "verifyFn" and any subsequent runtime
checks used to validate "res".
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: adbc554e-d641-4747-9b72-2e612961fbb6

📥 Commits

Reviewing files that changed from the base of the PR and between 371f9af and d774dc8.

📒 Files selected for processing (2)
  • apps/backend/src/lib/emailable.tsx
  • apps/backend/src/lib/users.tsx

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 26, 2026

Greptile Summary

This PR broadens country code validation and replaces the searchable dropdown UI with a plain text input, with the primary goal of supporting non-ISO two-letter codes (such as \"XX\" from Cloudflare for unknown/privacy-proxy IPs).

Key changes:

  • isValidCountryCode now accepts any /^[A-Z]{2}$/ string instead of checking against the 249-entry ISO 3166-1 alpha-2 list. The full list and validCountryCodeSet are removed.
  • createOrUpgradeAnonymousUserWithRules is simplified: the special signUpRuleOptions.countryCode !== null bypass is removed, and all paths now flow through getDerivedSignUpCountryCode. The merged requestCountryCode already carries signUpRuleOptions.countryCode via ??, so production behaviour is equivalent. In dev/test, email +tag derivation now takes precedence over an explicit signUpRuleOptions.countryCode (intentional per added tests).
  • getBrowserEndUserInfo in end-users.tsx now runs normalizeCountryCode on Vercel/Cloudflare geo headers before storing them, ensuring the stored value is always trimmed and uppercased.
  • The dashboard's CountryCodeSelect (combobox over 249 options) is replaced by CountryCodeInput (plain <Input> that auto-uppercases, strips non-alpha, and limits to 2 chars).

Notable semantic change: Any currently-deployed sign-up rules that relied on unrecognised geo codes (e.g. Cloudflare \"XX\") arriving as null will now see \"XX\" instead, since it passes the relaxed isValidCountryCode.

Confidence Score: 5/5

Safe to merge; all remaining findings are P2 style/UX observations with no blocking correctness issues.

The logic simplification in createOrUpgradeAnonymousUserWithRules is correct and an improvement (undefined-safety). The geo-header normalisation in end-users.tsx is a straightforward improvement. The isValidCountryCode relaxation is intentional and well-tested. The two P2 findings (potential "ZZ"/"XX" in rules, empty list entry UX) are informational, not blocking.

packages/stack-shared/src/utils/country-codes.ts — the isValidCountryCode change is the semantic core of this PR; worth a quick audit of any live sign-up rules before deploying.

Important Files Changed

Filename Overview
packages/stack-shared/src/utils/country-codes.ts Core semantic change: isValidCountryCode now accepts any /^[A-Z]{2}$/ pattern instead of the ISO 3166-1 alpha-2 list; the entire 249-entry list and validCountryCodeSet export are removed.
apps/backend/src/lib/users.tsx Simplified createOrUpgradeAnonymousUserWithRules to route all country codes through getDerivedSignUpCountryCode; switches to isValidCountryCode instead of validCountryCodeSet.has(); imports reorganized.
apps/backend/src/lib/end-users.tsx Geo-header country codes (Vercel/Cloudflare) are now normalized via normalizeCountryCode before storage in EndUserLocation, improving consistency.
apps/dashboard/src/components/country-code-select.tsx CountryCodeSelect (searchable dropdown over 249 ISO codes) replaced with CountryCodeInput (plain text input, uppercases & strips non-alpha, max 2 chars).
apps/dashboard/src/components/rule-builder/condition-builder.tsx Swapped CountryCodeSelect → CountryCodeInput and removed allowClear={false} prop; label copy updated from "ISO country code" to "2-letter country code".
packages/stack-shared/src/schema-fields.ts Removed ISO_3166_ALPHA_2_COUNTRY_CODES and validCountryCodeSet re-exports; countryCodeSchema error message updated to "2-letter country code".
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx CountryCodeSelect swapped to CountryCodeInput in the test-rules card; validation error message updated to "2-letter code".
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx CountryCodeSelect swapped to CountryCodeInput in the user detail panel's "Sign-up country code" field.
pnpm-lock.yaml Lockfile updated: @babel/core 7.26→7.29, nitro picks up rolldown, packages/private rimraf entry removed, eslint-module-utils peer specifiers resolved.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Sign-up Request] --> B{signUpRuleOptions.countryCode?}
    B -- set --> C[requestCountryCode = signUpRuleOptions.countryCode]
    B -- not set --> D{endUserRequestContext geo?}
    D -- present --> E[getBrowserEndUserInfo\nnormalizeCountryCode on header]
    D -- absent --> F[requestCountryCode = null]
    E --> G[requestCountryCode = normalized geo code]
    C --> H[getDerivedSignUpCountryCode]
    F --> H
    G --> H
    H --> I{dev/test + email tag?}
    I -- yes --> J[Extract tag from +XX@example.com]
    J --> K{isValidCountryCode tag?}
    K -- yes --> L[countryCode = tag]
    K -- no --> M[fall through]
    I -- no/prod --> M
    M --> N{isValidCountryCode requestCountryCode?}
    N -- yes --> O[countryCode = normalizedCode]
    N -- no --> P[countryCode = null]
    L --> Q[evaluateSignUpRules countryCode]
    O --> Q
    P --> Q
    Q --> R{shouldAllow?}
    R -- yes --> S[createOrUpgradeAnonymousUser]
    R -- no --> T[throw SignUpRejected]
Loading

Fix All in Claude Code Fix All in Cursor Fix All in Codex

Reviews (3): Last reviewed commit: "Enhance country code validation in visua..." | Re-trigger Greptile

@mantrakp04 mantrakp04 requested a review from N2D4 March 26, 2026 17:41
…esponses

- Updated `validateVerifyResponse` to throw errors for non-object responses and invalid states.
- Simplified response handling in `checkEmailWithEmailable` to ensure consistent error management.
- Adjusted tests to reflect changes in error handling for malformed Emailable responses.
- Replaced `CountryCodeSelect` with `CountryCodeInput` for a more streamlined input experience.
- Updated country code validation logic to ensure only 2-letter codes are accepted.
- Enhanced error messages for invalid country codes to improve clarity.
- Removed unused constants and simplified the country code validation functions.
- Adjusted tests to reflect changes in country code handling and validation.
@mantrakp04
Copy link
Copy Markdown
Collaborator Author

@greptile-ai review

@mantrakp04
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/dashboard/src/lib/cel-visual-parser.ts (1)

79-85: ⚠️ Potential issue | 🟠 Major

Reinstate fail-fast for numeric countryCode values

At Line 81, returning a number silently accepts an invalid state. This can emit semantically invalid rules (e.g., countryCode == "42") instead of surfacing the upstream type issue immediately.

Proposed fix
 function normalizeConditionValue(condition: ConditionNode): ConditionNode['value'] {
   if (condition.field !== 'countryCode') return condition.value;
-  if (typeof condition.value === 'number') return condition.value;
+  if (typeof condition.value === 'number') {
+    throw new StackAssertionError(
+      `countryCode must be a string or string[], received number: ${condition.value}`,
+    );
+  }
   return Array.isArray(condition.value)
     ? condition.value.map(normalizeCountryCode)
     : normalizeCountryCode(condition.value);
 }

As per coding guidelines, "NEVER silently use fallback values when type errors occur" and "Fail early, fail loud. Fail fast with an error instead of silently continuing."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/lib/cel-visual-parser.ts` around lines 79 - 85, In
normalizeConditionValue (for ConditionNode handling), don't silently accept
numeric values for field === 'countryCode'; instead detect typeof
condition.value === 'number' and throw a clear TypeError (including the field
name and the offending value) so callers fail fast; keep the existing
Array.isArray handling and normalizeCountryCode path for strings/arrays, but
replace the current numeric-return branch with an explicit thrown error to
surface upstream type issues.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@apps/dashboard/src/lib/cel-visual-parser.ts`:
- Around line 79-85: In normalizeConditionValue (for ConditionNode handling),
don't silently accept numeric values for field === 'countryCode'; instead detect
typeof condition.value === 'number' and throw a clear TypeError (including the
field name and the offending value) so callers fail fast; keep the existing
Array.isArray handling and normalizeCountryCode path for strings/arrays, but
replace the current numeric-return branch with an explicit thrown error to
surface upstream type issues.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c34add96-f699-46f9-81f9-0aaede409eef

📥 Commits

Reviewing files that changed from the base of the PR and between d774dc8 and ce2306e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • apps/backend/src/lib/end-users.tsx
  • apps/backend/src/lib/users.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/[userId]/page-client.tsx
  • apps/dashboard/src/components/country-code-select.tsx
  • apps/dashboard/src/components/rule-builder/condition-builder.tsx
  • apps/dashboard/src/lib/cel-visual-parser.ts
  • packages/stack-shared/src/schema-fields.ts
  • packages/stack-shared/src/utils/country-codes.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/backend/src/lib/users.tsx

- Updated `normalizeConditionValue` to throw an error for numeric values assigned to `countryCode`, ensuring that only string values are accepted.
- Improved error messaging for invalid country code inputs to enhance debugging and user feedback.
@mantrakp04
Copy link
Copy Markdown
Collaborator Author

@greptile-ai review

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.

1 participant