Source: https://www.hype-stack.dev/docs/development/troubleshooting

# Troubleshooting

Common problems when running a Hype Stack project, grouped by where they show up. Most come from environment files,
skipped migrations, or origin mismatches.

## Setup and install

### `pnpm install` fails or packages do not link

Run installs from the repository root. The repo is a single pnpm workspace. Installing inside one app breaks linking.
Use pnpm 12+, not npm or yarn (npm's `latest` tag still installs pnpm 11: use `npm install -g pnpm@12`).

### `error: too many arguments for 'create'`

An older CLI that `npx` cached. It only accepted `--name`; the current one takes the name as the first argument too.
Refresh with `npx -y @hype-stack/cli@latest create my-app`, or pass `--name my-app`.

### `Cannot find module .../pnpm.cjs`, or a shell syntax error from pnpm itself

The project pins pnpm 12, which is a native binary, and an older toolchain cannot reach it. A corepack that predates
pnpm 11 still looks for `bin/pnpm.cjs`, a file the package no longer ships, so every command dies with
`Cannot find module .../pnpm.cjs`. An older pnpm trying to switch versions on its own hits a different wall: it skips
the build script that unpacks the real binary and then executes the leftover placeholder, which surfaces as
`Syntax error: ")" unexpected` or `ENOEXEC`.

Either way the fix is the same: update the tool instead of letting it switch. Run `npm install -g corepack@latest` (if
you use corepack) or `npm install -g pnpm@12`, then re-run `pnpm install` in the project.

### Frontend cannot reach the API

Confirm `apps/frontend/.env` exists and `VITE_API_BASE_URL` points at the backend (default `http://localhost:3000`).
Restart the Vite dev server after changing `VITE_*` values; they are baked in at startup.

### Docker port already in use

Something else owns the Postgres or Valkey host port. Stop it, or change the published port in `docker-compose.yml` and
update `DATABASE_URL` / `VALKEY_URL` to match.

## Database

### Migrations cannot connect

Containers may still be starting. Run `docker compose ps` in `apps/backend` and wait for healthy, then retry
`pnpm --filter @hype-stack/backend migration:create`. Confirm `DATABASE_URL` in `apps/backend/.env`.

### Tables missing after `compose` or `template`

Pack installs merge Prisma schema and offer to run `prisma migrate dev` at the end. If you said no, or passed `--yes`
without a database up, nothing was applied. Run `pnpm --filter @hype-stack/backend migration:create`. See
[Migrations](/docs/backend/migrations).

### Types wrong after a schema change

Re-run the migrate / generate script so the Prisma client regenerates. Kysely reads those types.

## Auth

### Sign-in fails or callbacks bounce

Almost always an origin mismatch. Check:

- `FRONTEND_URL` on the backend
- `VITE_API_BASE_URL` on the frontend
- WorkOS redirect URIs or Better Auth `BETTER_AUTH_URL` + Google callback URLs

Every one of these must include the scheme. See
[Sign-in 404s with the frontend host inside the backend path](#sign-in-404s-with-the-frontend-host-inside-the-backend-path)
for what a missing `https://` looks like.

See [Authentication](/docs/backend/authentication) and the starter pack setup pages.

### Admin login rejects you

Add your email to `SUPER_ADMIN_EMAIL`, or have an existing super admin create an admin record. See
[Admin app](/docs/packs-templates/packs/admin).

### Stuck on onboarding

Private routes send users without an organization to `/onboarding`. Finish org creation or accept an invite. If you
already have an org and still loop, check `sdk.users.me` and the onboarding route guard.

## Billing

### Webhooks never update subscription state

For local dev with Stripe, run `stripe listen --forward-to localhost:3000/checkout/webhook` and put the printed
`whsec_...` into `STRIPE_WEBHOOK_SECRET`. Lemon Squeezy and Polar have no local forwarder; use a tunnel and register the
public URL in their dashboards. In production, use the live endpoint secret and confirm events reach
`/checkout/webhook`.

### Checkout works but receipts never arrive

Receipts require `RESEND_API_KEY`. Without it the pack skips email on purpose.

## Mobile

### Expo Go will not open the project

The App Store Expo Go is pinned to one SDK. This project needs a matching build from
[sign.expo.dev](https://sign.expo.dev/). Pick the SDK in `apps/mobile/package.json` (currently 57), sign with your Apple
ID, and install that binary. See [Mobile](/docs/mobile).

### The app loads, then closes with no error

Metro says the bundle built, the splash screen shows, and a second later the app is gone. No red screen, nothing in the
Metro log. That is a native crash, so there is no JavaScript error to find.

It almost always means a native module's JavaScript is on a different Expo SDK than the Expo Go you are running. Check
that every `expo-*` package in `apps/mobile/package.json` shares one major:

```bash
grep -E '"(expo|expo-|react-native)' apps/mobile/package.json
```

If one is behind, install the version that matches the rest and restart Metro with `--clear`. To confirm the diagnosis,
plug the phone into a Mac and read the crash report:
`Settings > Privacy & Security > Analytics & Improvements > Analytics Data`, newest `Expo Go-*.ips`. A `SIGSEGV` in
`cloneString` on the `com.facebook.react.runtime.JavaScript` thread is this bug.

### Phone cannot reach the API

`localhost` on the device is the phone, not your computer. Set `EXPO_PUBLIC_API_BASE_URL` in `apps/mobile/.env` to your
LAN IP (`http://192.168.x.x:3000`) and restart Metro.

## Deploy

### Provider CLI not logged in

`deploy web` checks before creating resources. Run `fly auth login` or `railway login`, or set `FLY_API_TOKEN` /
`RAILWAY_API_TOKEN` in CI. For `deploy mobile` it is `eas login` or `EXPO_TOKEN`; `deploy extension` reads store
credentials from the environment and prints the exact keys it is missing.

### Custom domain still serves the wrong origin

Set `url` on the target in `stack.json` and point DNS first. Update auth provider callbacks and `FRONTEND_URL` /
`VITE_API_BASE_URL` to the custom hosts. See [Deploy](/docs/cli/deploy).

### Sign-in 404s with the frontend host inside the backend path

Google login dead-ends on a URL that has the frontend domain wedged into the backend's callback path:

```
https://api.example.com/api/auth/callback/www.example.com/callback
```

Your frontend host showing up as a _path segment_ of the backend is the tell: an origin variable lost its scheme.
`FRONTEND_URL` is `www.example.com` rather than `https://www.example.com`.

`frontendUrl()` builds links by concatenation, so a bare host produces the relative string `www.example.com/callback`.
That reaches Better Auth as the OAuth `callbackURL` and comes back as a `Location` header, and a relative `Location` is
resolved against the URL that issued it - the backend's own callback route:

```
www.example.com/callback  +  base https://api.example.com/api/auth/callback/google
= https://api.example.com/api/auth/callback/www.example.com/callback
```

Railway's `RAILWAY_SERVICE_*_URL` variables are bare hosts, so copying one into `FRONTEND_URL`, `ADMIN_URL`, or
`VITE_ADMIN_URL` reproduces this exactly.

The same value breaks two more things with no error at all: the CORS allow-list and Better Auth's `trustedOrigins`
compare against `Origin` request headers, which always carry a scheme, so a bare host matches nothing and cross-origin
requests simply stop working.

Give every origin variable a full origin - scheme included, no trailing slash - and redeploy: `FRONTEND_URL`,
`ADMIN_URL`, `SERVER_URL`, `BETTER_AUTH_URL`. The backend prints its resolved value at boot, and CORS should echo the
frontend origin back:

```bash
curl -sI -H "Origin: https://www.example.com" https://api.example.com/ping/x | grep -i access-control-allow-origin
```

### A corrected `VITE_*` value did not take effect

`VITE_*` variables are inlined into the bundle at build time, so fixing one in the deploy provider does nothing until
the frontend service **rebuilds**. A restart redeploys the old bundle with the old value. Backend `process.env` reads
are per-boot and only need a restart, which is why the same fix can appear to land on one service and not the other.

### Migrations failed on release

Read the provider log for the release / pre-deploy command. Fix the SQL or connection string, then redeploy. Use
`--skip-migrations` only when you intend to migrate out of band.

## Related

- [Installation](/docs/getting-started/installation)
- [Mobile](/docs/mobile)
- [Going to production](/docs/getting-started/going-to-production)
- [Env variables](/docs/backend/env-variables)
