Most of these cost an hour or more on the day itself, which is the only reason they are worth writing down.
The deployment target was wrong, and I found out with the app already built. Next.js 16 renamed middleware.ts to proxy.ts and made it Node-runtime only; @opennextjs/cloudflare refuses Node middleware. Each tool rejected the other by name: "Node.js middleware is not currently supported" from the adapter, "Proxy always runs on Node.js runtime" from Next when I tried to force edge. proxy.ts carries the per-request CSP nonce, and without it the CSP needs unsafe-inline, which makes it close to decorative — so dropping it was not an option. Moved to Vercel; the domain stayed on Cloudflare.
Email confirmation links do not survive contact with real inboxes. The first real signup after deploying failed with otp_expired. Mail scanners and link previewers fetch every URL in an incoming message, and a one-time token is spent by that fetch — the person then clicks a link that has already been used. Codes fixed it: a code cannot be spent by a machine that merely reads the email.
I nearly shipped an account-takeover path inside a nicety. I put the verification code in the email subject, for the notification preview. sendEmail writes every subject to email_logs, and admins can read that table — so any admin could have read a password-reset code and taken over the account, leaving the victim only a password that stopped working. The body is never stored; the code lives there now.
revoke all ... from public does not remove Supabase's grants to anon and authenticated. users_due_for_reminder was callable with the public anon key and returned every active user's email address.
A row policy cannot hide a column. Latitude sits on the same row as the timestamp, so "share attendance but not location" is not expressible in RLS — an earlier version kept the row policies and relied on the app calling the right functions, which anyone could bypass from devtools. Partners now have no direct read at all.
React 19 resets an uncontrolled form once its action resolves, so a client effect reading the email input always saw an empty field. Related: Playwright's auto-waiting does not cover hydration — setInputFiles before React attaches its handler fires an event with nobody listening, the call succeeds, and nothing happens. slowMo hid it, so the test passed headed and failed headless.
create or replace function cannot change a function's return type; adding three columns failed a migration halfway with 42P13.
Vercel's Hobby plan runs cron once a day, which cannot serve per-user, per-timezone reminders. That schedule moved to GitHub Actions.