|
| 1 | +--- |
| 2 | +title: "Production Variant Routes Redirect to Base URL" |
| 3 | +type: "deployment-issue" |
| 4 | +severity: "high" |
| 5 | +status: "resolved" |
| 6 | +date_identified: "2026-01-13" |
| 7 | +date_resolved: "2026-01-13" |
| 8 | + |
| 9 | +category: "deployment-issue" |
| 10 | +component: "Variant Portfolio (src/pages/VariantPortfolio.tsx)" |
| 11 | +subsystem: "Environment configuration & variant publishing" |
| 12 | + |
| 13 | +symptoms: |
| 14 | + - "Navigating to variant URLs (e.g., edgeoftrust.com/notion/senior-product-manager) shows 'Loading...' then redirects to '/'" |
| 15 | + - "Console shows 404 error for the route" |
| 16 | + - "React app loads successfully with 200 status on all assets" |
| 17 | + - "Variant data exists in Convex database but is not displayed" |
| 18 | + |
| 19 | +root_cause: | |
| 20 | + Two-part failure in variant delivery pipeline: |
| 21 | + 1. GitHub Actions secret VITE_CONVEX_URL pointed to wrong Convex instance (happy-otter-123.convex.cloud instead of scintillating-husky-549.convex.cloud) |
| 22 | + 2. Variant publishStatus was "draft" instead of "published" - the getBySlug query filters unpublished variants |
| 23 | +
|
| 24 | +affected_files: |
| 25 | + - path: ".github/workflows/deploy.yml" |
| 26 | + description: "GitHub Actions workflow referencing VITE_CONVEX_URL secret" |
| 27 | + - path: "src/pages/VariantPortfolio.tsx" |
| 28 | + description: "Page component that loads variant data via useVariant hook" |
| 29 | + - path: "src/lib/variants.ts" |
| 30 | + description: "useVariant hook that fetches variant from Convex" |
| 31 | + - path: "convex/variants.ts" |
| 32 | + description: "getBySlug query that filters by publishStatus === 'published'" |
| 33 | + |
| 34 | +tags: [deployment, environment-configuration, convex, variant-routing, github-actions, database-status] |
| 35 | +--- |
| 36 | + |
| 37 | +# Production Variant Routes Redirect to Base URL |
| 38 | + |
| 39 | +## Problem |
| 40 | + |
| 41 | +Variant portfolio routes (e.g., `edgeoftrust.com/notion/senior-product-manager`) were showing "Loading..." briefly then redirecting to the home page instead of displaying the personalized portfolio variant. |
| 42 | + |
| 43 | +### Symptoms |
| 44 | + |
| 45 | +1. Page shows "Loading..." text briefly |
| 46 | +2. Immediately redirects to `/` (home page) |
| 47 | +3. Console shows 404 error for the route itself |
| 48 | +4. All JavaScript assets load successfully (200 status) |
| 49 | +5. Variant data confirmed to exist in Convex database |
| 50 | + |
| 51 | +## Investigation |
| 52 | + |
| 53 | +### Step 1: Verify SPA Routing |
| 54 | +```bash |
| 55 | +# Check if 404.html exists (needed for GitHub Pages SPA routing) |
| 56 | +curl -s "https://edgeoftrust.com/404.html" | head -20 |
| 57 | +# Result: 404.html exists and contains the SPA shell |
| 58 | +``` |
| 59 | + |
| 60 | +### Step 2: Check Network Requests |
| 61 | +Browser dev tools showed all assets loading correctly. The issue was not with asset delivery. |
| 62 | + |
| 63 | +### Step 3: Verify Variant Exists in Database |
| 64 | +```bash |
| 65 | +npx convex run variants:listAll | grep -i "notion" |
| 66 | +# Result: Variant exists with slug "notion-senior-product-manager" |
| 67 | +``` |
| 68 | + |
| 69 | +### Step 4: Compare Convex URLs |
| 70 | +```bash |
| 71 | +# Check URL in production bundle |
| 72 | +curl -s "https://edgeoftrust.com/assets/index-*.js" | grep -oE "https://[a-z0-9-]+\.convex\.cloud" |
| 73 | +# Result: https://happy-otter-123.convex.cloud |
| 74 | + |
| 75 | +# Check local .env.local |
| 76 | +cat .env.local | grep VITE_CONVEX_URL |
| 77 | +# Result: https://scintillating-husky-549.convex.cloud |
| 78 | +``` |
| 79 | + |
| 80 | +**Finding**: Production bundle had wrong Convex URL! |
| 81 | + |
| 82 | +### Step 5: Check Variant Publish Status |
| 83 | +```bash |
| 84 | +npx convex run variants:listAll | grep -A5 "notion" |
| 85 | +# Result: publishStatus: "draft" |
| 86 | +``` |
| 87 | + |
| 88 | +**Finding**: Variant was not published! |
| 89 | + |
| 90 | +## Root Causes |
| 91 | + |
| 92 | +### Cause 1: Wrong Convex URL in GitHub Secret |
| 93 | + |
| 94 | +The `VITE_CONVEX_URL` GitHub Actions secret was set to an old/test Convex deployment: |
| 95 | +- **Production had**: `https://happy-otter-123.convex.cloud` |
| 96 | +- **Should have been**: `https://scintillating-husky-549.convex.cloud` |
| 97 | + |
| 98 | +This caused the production app to query a different Convex database that didn't have the variant data. |
| 99 | + |
| 100 | +### Cause 2: Variant Not Published |
| 101 | + |
| 102 | +The `getBySlug` query in `convex/variants.ts` filters by publish status: |
| 103 | + |
| 104 | +```typescript |
| 105 | +// convex/variants.ts:32 |
| 106 | +if (!variant || variant.publishStatus !== "published") { |
| 107 | + return null; |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +The variant had `publishStatus: "draft"`, so the query returned `null`, triggering the redirect in `VariantPortfolio.tsx`: |
| 112 | + |
| 113 | +```typescript |
| 114 | +// src/pages/VariantPortfolio.tsx:52-54 |
| 115 | +if (!variant) { |
| 116 | + return <Navigate to="/" replace />; |
| 117 | +} |
| 118 | +``` |
| 119 | + |
| 120 | +## Solution |
| 121 | + |
| 122 | +### Fix 1: Update GitHub Secret |
| 123 | + |
| 124 | +```bash |
| 125 | +# Delete and recreate the secret with correct URL |
| 126 | +gh secret delete VITE_CONVEX_URL --repo fotescodev/portfolio |
| 127 | +gh secret set VITE_CONVEX_URL --body "https://scintillating-husky-549.convex.cloud" --repo fotescodev/portfolio |
| 128 | + |
| 129 | +# Trigger redeployment |
| 130 | +gh workflow run "Deploy to GitHub Pages" --repo fotescodev/portfolio |
| 131 | +``` |
| 132 | + |
| 133 | +### Fix 2: Publish the Variant |
| 134 | + |
| 135 | +```bash |
| 136 | +npx convex run variants:updateStatus '{"slug": "notion-senior-product-manager", "publishStatus": "published"}' |
| 137 | +``` |
| 138 | + |
| 139 | +### Verification |
| 140 | + |
| 141 | +```bash |
| 142 | +# Confirm variant is now published |
| 143 | +npx convex run variants:listAll | grep -A2 "notion" |
| 144 | +# publishStatus: "published" |
| 145 | + |
| 146 | +# Test production URL |
| 147 | +curl -sI "https://edgeoftrust.com/notion/senior-product-manager" |
| 148 | +# Page loads correctly with variant content |
| 149 | +``` |
| 150 | + |
| 151 | +## Prevention |
| 152 | + |
| 153 | +### 1. Pre-Deployment Checklist |
| 154 | + |
| 155 | +- [ ] Verify `VITE_CONVEX_URL` secret matches production Convex deployment |
| 156 | +- [ ] Run `npx convex run variants:listAll` to confirm target variants are published |
| 157 | +- [ ] Test variant URL locally before deploying |
| 158 | + |
| 159 | +### 2. Convex URL Validation Script |
| 160 | + |
| 161 | +```typescript |
| 162 | +// scripts/verify-convex-url.ts |
| 163 | +const expectedUrl = "https://scintillating-husky-549.convex.cloud"; |
| 164 | +const envUrl = process.env.VITE_CONVEX_URL; |
| 165 | + |
| 166 | +if (envUrl !== expectedUrl) { |
| 167 | + console.error(`VITE_CONVEX_URL mismatch!`); |
| 168 | + console.error(`Expected: ${expectedUrl}`); |
| 169 | + console.error(`Got: ${envUrl}`); |
| 170 | + process.exit(1); |
| 171 | +} |
| 172 | +``` |
| 173 | + |
| 174 | +### 3. Variant Status Validation |
| 175 | + |
| 176 | +```typescript |
| 177 | +// scripts/check-variant-publishing.ts |
| 178 | +// Run before deployment to ensure target variants are published |
| 179 | +const draftVariants = variants.filter(v => v.publishStatus === "draft"); |
| 180 | +if (draftVariants.length > 0) { |
| 181 | + console.warn("Draft variants found:", draftVariants.map(v => v.slug)); |
| 182 | +} |
| 183 | +``` |
| 184 | + |
| 185 | +### 4. Post-Deploy Verification |
| 186 | + |
| 187 | +After each deployment, verify: |
| 188 | +1. Production bundle contains correct Convex URL |
| 189 | +2. At least one variant route loads successfully |
| 190 | +3. Resume links work (or fall back gracefully) |
| 191 | + |
| 192 | +## Related Issues |
| 193 | + |
| 194 | +- `docs/solutions/integration-issues/react-router-static-file-interception.md` - SPA routing issues |
| 195 | +- `docs/solutions/integration-issues/cv-dashboard-dev-port-configuration.md` - DEV mode configuration |
| 196 | + |
| 197 | +## Key Learnings |
| 198 | + |
| 199 | +1. **Environment variables are embedded at build time** - Vite bakes `VITE_*` vars into the bundle. Changing a secret requires a rebuild. |
| 200 | + |
| 201 | +2. **Two-stage variant delivery** - Variants must be both: |
| 202 | + - Stored in the correct Convex deployment (via `VITE_CONVEX_URL`) |
| 203 | + - Published (`publishStatus: "published"`) |
| 204 | + |
| 205 | +3. **Silent failures are dangerous** - The redirect to home gave no indication of the actual problem. Consider adding error states or logging for debugging. |
0 commit comments