Troubleshooting
Common issues and solutions for Roxabi Boilerplate development
Common issues encountered during development and their solutions.
Database Connection Failures
Symptom: connection refused or ECONNREFUSED errors on port 5432 when starting the API or running migrations.
Cause: The PostgreSQL Docker container is not running, or is mapped to a different port than the app expects.
Fix: Start the Docker services and verify the container is healthy:
docker compose up -d
docker compose psConfirm the DATABASE_URL in your .env file matches the port and credentials defined in docker-compose.yml.
If you copied .env.example but changed nothing, verify the default credentials (roxabi:roxabi) match what docker-compose.yml defines. A common mistake is editing the password in one place but not the other:
# Verify by connecting manually
docker exec -it roxabi-postgres psql -U roxabi -d roxabi -c "SELECT 1"Bun Version Mismatch
Symptom: error: package manager mismatch during install, or unexpected runtime behavior.
Cause: Your local Bun version does not match the packageManager field declared in the root package.json.
Fix: Upgrade Bun to the latest version, or install the exact version the project expects:
bun upgradeIf the project pins a specific version, check package.json for the packageManager field and install that version.
Port Already in Use
Symptom: EADDRINUSE error when starting the dev server or API.
Cause: Another process is already listening on port 3000 (web), 4000 (API), or 3001 (email preview).
Fix: Kill the process occupying the port, or start on a different port:
lsof -ti:3000 | xargs -r kill
lsof -ti:4000 | xargs -r kill
lsof -ti:3001 | xargs -r killAlternatively, set the PORT environment variable to use a different port.
Dependency Install Failures After Updates
Symptom: bun install fails with resolution errors, checksum mismatches, or bun.lock conflicts after pulling new changes or updating dependencies.
Cause: The local node_modules tree or bun.lock file is out of sync with the updated package.json files. This commonly happens after merging a branch that changed dependencies, or after a major Bun version upgrade that changes the lockfile format.
Fix:
Remove node_modules across the entire monorepo and reinstall from scratch:
# Remove all node_modules directories
rm -rf node_modules apps/*/node_modules packages/*/node_modules
# Regenerate the lockfile and install
bun installIf the lockfile itself is corrupted or has unresolvable conflicts:
rm bun.lock
bun installWarning: Deleting
bun.lockchanges resolved dependency versions. Only do this if the lockfile has unresolvable merge conflicts. Runbun run typecheck && bun run testafterward to catch any version drift.
After reinstalling, verify everything builds:
bun run typecheck && bun run buildAuthentication Not Working in Development
Symptom: Login succeeds (API returns 200) but the frontend still shows the user as unauthenticated. useSession() returns null. Requests to protected routes return 401 Unauthorized.
Cause: Better Auth uses cookie-based sessions. In development, the frontend (port 3000) and API (port 4000) run on different ports. The dev proxy configured in vite.config.ts at /api/** forwards requests to the API so that cookies stay on the same origin (localhost:3000). If the proxy is not active or the frontend client sends requests directly to localhost:4000, cookies are set on a different origin and the browser will not include them in subsequent requests.
Another common cause is a missing or default BETTER_AUTH_SECRET in .env. If the secret is not set, session tokens cannot be signed correctly.
Fix:
-
Make sure
bun devis running for both the web and API apps (or usebun devat the monorepo root, which starts both). -
Confirm the auth client in
apps/web/src/lib/authClient.tsuseswindow.location.origin(not the API URL directly) as thebaseURL. The default configuration does this correctly -- if you've modified it, revert to usingwindow.location.origin.
If the API fails to start with a secret-related error, see BETTER_AUTH_SECRET Errors on Startup below.
- Check that the dev proxy is forwarding
/api/**requests. Open DevTools Network tab, sign in, and confirm requests go tolocalhost:3000/api/auth/*(notlocalhost:4000).
For a full explanation of the cookie-based auth architecture, see the Authentication guide.
CORS Errors
Symptom: Browser console shows Access to fetch at 'http://localhost:4000/...' has been blocked by CORS policy or similar cross-origin errors.
Cause: The API's CORS_ORIGIN environment variable does not include the origin the browser is sending requests from. In development, CORS_ORIGIN defaults to http://localhost:3000. If you are accessing the frontend on a different host (e.g., 127.0.0.1:3000 or a custom domain), the origins will not match.
Another cause is making requests directly to the API (port 4000) from frontend code instead of going through the dev proxy. The proxy eliminates CORS entirely in development because both the page and the API appear to be on the same origin.
Fix:
-
Ensure you access the frontend at
http://localhost:3000(not127.0.0.1or another hostname). -
Verify
CORS_ORIGINin.envmatches the exact origin you use in the browser:
# .env
CORS_ORIGIN=http://localhost:3000For multiple origins (e.g., staging and local), use a comma-separated list:
CORS_ORIGIN=http://localhost:3000,https://staging.example.com-
If you need to call the API directly (bypassing the proxy), ensure
CORS_ORIGINincludes the origin your frontend is served from (e.g.,http://localhost:3000). The value must match the browser's origin, not the API's address. -
In production, set
CORS_ORIGINto your exact frontend domain in the Vercel API project settings:
CORS_ORIGIN=https://app.yourdomain.comIn production, if CORS_ORIGIN contains only *, CORS will be disabled entirely (all cross-origin requests blocked). If * appears alongside valid origins, only the wildcard entry is removed. The API logs a warning in both cases.
BETTER_AUTH_SECRET Errors on Startup
Symptom: The API crashes on startup with an error about BETTER_AUTH_SECRET being invalid or set to a known default value.
Cause: In production (NODE_ENV=production), Better Auth validates that the secret is not a placeholder. The .env.example ships with BETTER_AUTH_SECRET=change-me-to-a-random-32-char-string, which is rejected in production mode.
Fix:
Generate a cryptographically random secret and set it in your .env:
# Generate a 32-character random secret
openssl rand -base64 32Update your .env:
BETTER_AUTH_SECRET=<paste-your-generated-secret-here>For Vercel deployments, set it in the API project's environment variables dashboard. A value of at least 32 characters is recommended.
For the full list of auth-related environment variables, see the Authentication guide.
Worktree Database Not Found
Symptom: After creating a git worktree, the API fails to start with a database error like database "roxabi_<number>" does not exist or migrations fail.
Cause: Each worktree is intended to use its own isolated branch database (roxabi_<issue_number>). The db:branch:create script creates this database, runs migrations, seeds it, and updates the worktree's .env with the correct DATABASE_URL. If you skipped this step, the worktree's .env still points to the default roxabi database (or worse, no database at all).
Fix:
Important: Run this from inside your worktree directory (e.g., ../roxabi-42/), not from the main repository checkout. Running from the wrong directory will update the wrong .env file.
cd apps/api
bun run db:branch:create --force <issue_number>This will:
- Create the
roxabi_<issue_number>database in the Docker container - Run all migrations
- Seed the database with dev data (
dev@roxabi.local/password123) - Update your
.envDATABASE_URLto point to the new database
If you already started working and need to check which branch databases exist:
cd apps/api
bun run db:branch:listTo clean up a branch database you no longer need:
cd apps/api
bun run db:branch:dropFor more on branch databases, see Configuration -- Branch Databases.
After Pulling Admin Panel Changes (PR #279)
If you had an existing Docker Postgres setup before this PR, the roxabi_app database user
was not created automatically (Docker init scripts only run on first boot). Run:
bun run db:setup-app-userThis creates the roxabi_app user with RLS-enforced permissions. Without it, tenant-scoped
queries will fail with "permission denied to set role app_user".
Alternative: delete your Docker volume and recreate: bun run db:down && docker volume rm roxabi_boilerplate_postgres_data && bun run db:up
Pre-commit Hook Failures
Symptom: Commit is rejected by Lefthook with Biome formatting errors or commitlint violations.
Cause: Staged files have formatting issues, or the commit message does not follow the Conventional Commits format.
Fix: For formatting issues, run Biome auto-fix across the project:
bunx biome check --write .For commit message rejections, use the Conventional Commits format: type(scope): description (e.g., feat(web): add login page). See the Contributing guide for the full list of types.
TypeScript Build Errors After Dependency Updates
Symptom: Type errors appear after running bun install or updating packages, even though the code has not changed.
Cause: Stale .tsbuildinfo incremental compilation files or cached type definitions from previous dependency versions.
Fix: Clear the build cache and re-run the type checker:
# Remove stale incremental compilation files
find . -name '*.tsbuildinfo' -not -path '*/node_modules/*' -delete
bun run clean:cache && bun run typecheckTurbo Cache Staleness
Symptom: Build succeeds but your changes do not appear in the output, or Turbo reports "cache hit" for code you just modified.
Cause: Turbo's content hash does not capture all inputs (e.g., environment variables or files outside the declared inputs).
Fix: Clear the Turbo cache and rebuild:
bun run clean:cacheOr remove the cache directory manually:
rm -rf .turboThen run your build command again.