name: tanstack-start-vps-debug
description: Debug DocCorretor TanStack Start VPS app when browser automation is blocked by Google OAuth
TanStack Start VPS Debug — DocCorretor
Debugging TanStack Start (Lovable-style) app deployed on VPS when browser automation is blocked.
Context
- - App: DocCorretor (
documentos.rochasalesseguros.com.br) - - Two app dirs:
/var/www/documentos2/app/(active) and/var/www/deploy-vps-nodejs/app/(reference, 376-line) - - Browser automation blocked by Google auth → all investigation via terminal only
- - Active app:
/var/www/documentos2/app/ - - Build:
npm run build(TanStack Start) - - Service:
sudo systemctl restart documentos - - Logs:
sudo journalctl -u documentos -n 50 - - Assets after build: ensure
dist/server/assets/exists
Common Issues & Fixes
Admin tabs not showing
Symptom: User with admin role can't see admin panels.
Root cause: useAuth.ts checked roles.includes("admin") but DB stores role as "adm".
Fix: Patch useAuth.ts:
`
roles.includes("admin") → roles.includes("adm")
`
"Link para corretor" button opens dialog but nothing happens
Symptom: Button in header clicks, dialog opens, but "Gerar link" does nothing. No toast, no error.
Root cause: Another button's onClick had e.preventDefault() that was blocking the dialog's open state. Found by grepping compiled bundle.
Fix: Changed header button to toast.info("Selecione um cliente na lista para gerar o link.") pointing user to card-level "Link" button.
Button onClick calls e.preventDefault() silently blocking UI
Symptom: Clicking a button appears to do nothing (no toast, no navigation, no dialog).
How to find: Grep compiled bundle for the button's handler function name, then search for preventDefault near it:
`bash
grep -o 'FUNCTION_NAME[^}]preventDefault[^}]}' dist/client/assets/*.js
`
Or simpler — grep all preventDefault calls:
`bash
grep -o '[^;]preventDefault[^;];' dist/client/assets/*.js
`
client_upload_links insert fails with 42501
Symptom: RLS policy violation on INSERT to client_upload_links.
Fix:
1. Add created_by uuid column (no FK — profiles.id not unique across auth systems)
2. Set expires_at default: NOW() + INTERVAL '30 days'
3. Create RLS policies:
- INSERT: auth.role() = 'authenticated' AND auth.uid() = created_by
- SELECT (anon): expires_at > NOW()
- UPDATE (anon): used_at IS NULL
Blank Screen (Tela Preta) — SSR Returning Empty Markers Only
Symptom: Site loads (200 OK), HTML has TanStack Router markup (tsr, $_TSR.router), scripts load (200), but body is completely empty — just SSR streaming markers with no actual content. Browser shows blank white page.
Diagnosis:
1. curl -s http://127.0.0.1:3010/ — if body has with no content, SSR is failing silently
2. curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:3010/assets/index-BnIEtwWC.js" — if 404, Node isn't serving assets (nginx serves fine)
3. stat dist/client/assets/index-*.js vs stat src/routes/index.tsx — build date vs source date mismatch
Root Cause: TanStack Start build got into a corrupted state — SSR output was incomplete. Can happen after: partial deploys, disk full during build, Node version changes, or TanStack package updates.
Fix — REBUILD:
`bash
cd /var/www/documentos2/app
export PATH="/root/.nvm/versions/node/v20.20.2/bin:$PATH"
/root/.nvm/versions/node/v20.20.2/bin/node /root/.nvm/versions/node/v20.20.2/bin/npm run build
sudo systemctl restart documentos
`
Verification: After rebuild, curl -s http://127.0.0.1:3010/ should show actual HTML content (not just empty markers). Browser should show login page, not blank screen.
Note: RequireAuth causes empty SSR by design (useEffect doesn't run server-side) — but that's expected and the page still renders with the login UI. The blank screen bug is different: SSR returns literally nothing between the streaming markers.
Bundle mismatch — assets in wrong folder
Symptom: SSR page works but /_serverFn 500s.
Fix: After build, copy assets to subdir:
`bash
cp -r dist/server/* dist/server/assets/
`
Build times out / corrupts dist (ComercialRS pattern)
Symptom: Browser console shows 404 on chunks like OnboardingTutorial-DiDRN2MO.js and filter-DrFHscE8.js. Login fails with TypeError: Failed to fetch dynamically imported module.
Root cause 1: Build takes 6+ minutes and times out when run in foreground. Vite clears the dist/ folder before finishing, leaving it without index.html.
Root cause 2: TanStack Start generates root.html inside dist/src/, NOT index.html at the root of dist/. Nginx is configured to serve index.html from the root.
Fix:
`bash
Run build in background with nohup (never foreground with timeout)
cd /var/www/comercialrs
PATH="/root/.nvm/versions/node/v20.20.2/bin:$PATH" nohup /root/.nvm/versions/node/v20.20.2/bin/node ./node_modules/vite/bin/vite.js build > /tmp/comercialrs_build.log 2>&1 &
Wait for build to finish (check log)
sleep 5 && tail /tmp/comercialrs_build.log
Look for "✓ built in Xm Xs" at the end
After successful build: TanStack Start puts root.html in dist/src/
Copy to index.html so nginx can serve it
cp /var/www/comercialrs/dist/src/root.html /var/www/comercialrs/dist/index.html
Verify site is up
curl -sI "https://comercialrs.rochasalesseguros.com.br/" | head -3
`
Prevention: Always use nohup + background for Vite builds on VPS. The build MUST complete before nginx can serve the app correctly.
Key Commands
`bash
Build ComercialRS (Node 20, background, ~6 min)
cd /var/www/comercialrs && PATH="/root/.nvm/versions/node/v20.20.2/bin:$PATH" nohup /root/.nvm/versions/node/v20.20.2/bin/node ./node_modules/vite/bin/vite.js build > /tmp/comercialrs_build.log 2>&1 &
Monitor build
tail -f /tmp/comercialrs_build.log
After build: fix index.html location (TanStack Start quirk)
cp /var/www/comercialrs/dist/src/root.html /var/www/comercialrs/dist/index.html
Check RLS policies on a table
curl -s -X POST "https://api.supabase.com/v1/projects/dauftiqcvgaydddoxqhh/database/query" \
-H "Authorization: Bearer sbp_..." \
-H "Content-Type: application/json" \
-d '{"query": "SELECT policyname, cmd, qual FROM pg_policies WHERE tablename = '\''tasks'\';"}'
Grep compiled bundle for specific function behavior
grep -o 'FUNCTION_NAME[^}]preventDefault[^}]}' dist/assets/*.js
Check what's in SSR bundle (strings from compressed HTML)
curl -s --compressed "https://comercialrs.rochasalesseguros.com.br/" | strings | grep -i "Onboarding"
Debug Pattern (Browser Blocked)
When browser automation is blocked by auth, debug entirely via terminal:
1. grep compiled JS bundles for function names / strings
2. curl --compressed SSR pages to see what's rendered
3. strings on minified bundles to find code patterns
4. Direct REST API calls to test RLS/DB behavior
5. Ask user to check browser DevTools console for errors