Troubleshooting
Search this page for the message you see.
Starting up
Section titled “Starting up”The API checks its variables at startup and stops with Invalid environment variables:,
followed by one line per problem.
| Message | Fix |
|---|---|
AUTH_SECRET: must be at least 32 characters |
Generate one: openssl rand -base64 32 |
AUTH_SECRET: is still the example value; generate a real one for production |
Same, in production or production:up |
Set AUTH_SECRET in .env. Generate one with openssl rand -base64 32 |
production:up found no AUTH_SECRET in .env |
STRIPE_WEBHOOK_SECRET: is required with STRIPE_SECRET_KEY |
Run bun run billing:listen and put the whsec_… it prints in .env |
POLAR_WEBHOOK_SECRET: is required with POLAR_ACCESS_TOKEN |
Add a webhook endpoint in Polar and put its secret in .env: Polar |
BILLING_PROVIDER: is stripe, but POLAR_ACCESS_TOKEN is set (or the other way round) |
Set BILLING_PROVIDER to the provider whose key you set, and empty the other key |
GOOGLE_CLIENT_SECRET: set both GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET, or neither |
You set one half of a provider |
OTEL_EXPORTER_OTLP_HEADERS: must be name=value pairs, comma-separated |
Write it as x-honeycomb-team=abc123,other=value |
MIXPANEL_TOKEN: must be a Mixpanel project token… |
Use the project token from the project’s settings, not its API secret or a service account |
POSTHOG_KEY: must be a PostHog project API key (phc_…)… |
Use the project API key from the project’s settings, not a personal API key (phx_…) |
ANALYTICS_PROVIDER: is mixpanel, but POSTHOG_KEY is set (or the other way round) |
Set ANALYTICS_PROVIDER to the provider whose key you set, and empty the other key |
SENTRY_DSN: Invalid URL |
Copy the DSN from the project’s Settings > Client Keys: it starts with https:// |
[sentry-vite-plugin] Error while building the web app |
Source maps were not sent: check SENTRY_AUTH_TOKEN, SENTRY_ORG and SENTRY_WEB_PROJECT |
STAND_INS: is for tests on this machine or in CI… |
It replaces Stripe and the model for the browser journeys. Leave it empty |
STAND_INS: replaces Stripe, Polar and the model… |
The journeys run without real keys: docker-compose.acceptance.yml empties them |
LOG_LEVEL: Invalid option |
Use fatal, error, warn, info, debug, trace or silent |
Log lines Redis: connect ECONNREFUSED, or checks.redis is down |
Start Redis: bun run services:up. Elsewhere, check REDIS_URL |
DATABASE_URL: Invalid URL, or the API can’t connect |
Start the database: bun run services:up |
production:up fails on a port |
Something uses 8080, 8081 or 8026. Change it in docker-compose.production.yml |
docker:dev fails with port is already allocated |
bun dev is running too. Stop one of them: they use the same ports |
Signing in
Section titled “Signing in”| Message or symptom | Fix |
|---|---|
| The verification email never arrives, locally | It is in Mailpit, http://localhost:8025, not your inbox |
| Signing in leads to “Verify your email” | The address isn’t verified. A new link was sent |
| “This link no longer works” | Reset links work once, for an hour. Ask for a new one |
401 Sign in to continue |
No session: signed out, or the cookie wasn’t sent |
403 on every call to /api/auth |
The browser’s address is not WEB_URL or in CORS_ORIGINS |
403 This account has been suspended… |
An admin suspended it. See Admin panel |
429 Too many requests. Please try again later. |
The sign-in rate limit, in production |
| No Google or GitHub button | One of its two values is missing, or the API wasn’t restarted |
You are the only owner of … before deleting your account. |
Make someone else owner, or delete the workspace first |
No Admin in the menu, or 403 Only admins can do this |
Your address isn’t in ADMIN_EMAILS or isn’t verified. Or you’re signed in as someone else: stop first |
401 This API key is not valid… |
The key was revoked or has expired, or was copied wrong. Create a new one |
403 API keys can only call the routes of their workspace. |
Keys reach /api/workspaces/<their workspace>/… only. See API keys |
400 That URL is taken. Choose another. |
Another workspace has that address, or is being made with it at this moment: pick another |
403 API keys cannot do this. Sign in to do it. |
The route is for people: it reads @CurrentUser() or is marked @NoApiKeys() |
429 Too many requests with this API key… |
The key’s rate limit: wait the seconds in Retry-After |
Log line Rate limiting could not determine a client IP |
The API gets no single visitor address. See Other hosts |
Mobile app
Section titled “Mobile app”| Message or symptom | Fix |
|---|---|
Cannot reach the server |
The API is not running, or EXPO_PUBLIC_API_URL is wrong. On a phone, localhost is the phone |
| Signing in from the app answers 403 | MOBILE_SCHEME is not the scheme in apps/mobile/app.json |
Notifications are not set up for this build |
Run bunx eas-cli init in apps/mobile: see Mobile builds |
Notifications need a real phone |
A simulator cannot receive them. Use a development build on a phone |
Cannot find native module 'ExpoAsset' in Expo Go |
Expo was started for a build of your own. Use bun run dev:mobile, which starts it for Expo Go |
| A link to a new screen does not type-check | Start bun run dev:mobile once: it writes the list of screens |
Billing
Section titled “Billing”| Message or symptom | Fix |
|---|---|
| The billing page says billing is not set up | Set STRIPE_SECRET_KEY, or POLAR_ACCESS_TOKEN with BILLING_PROVIDER=polar |
503 Stripe has no price "pro_month" |
Run bun run billing:sync |
503 Polar has no product for pro, monthly |
Run bun run billing:sync with BILLING_PROVIDER=polar |
Manage fails with Polar’s 403 insufficient_scope |
With teams on, the token needs read access to members: see Polar |
| You paid and the plan didn’t change | The webhook didn’t arrive. Locally keep billing:listen running; in production check the endpoint in Stripe |
400 Invalid webhook signature |
STRIPE_WEBHOOK_SECRET is from another endpoint, or test mode with a live key |
402 … is part of the Pro plan. Upgrade to use it. |
The plan lacks that feature. See Charge for a feature |
402 You have used the … included this month |
The free plan’s allowance is used up. See Charge for usage |
402 You have used the … your plan includes until |
The plan has no price beyond its allowance (pricePerUnit: null) |
503 Stripe has no price "pro_month_aiMessages" |
A plan charges for a meter that has no price yet. Run bun run billing:sync |
| Usage beyond the allowance is not on the invoice | The subscription is older than the meter’s price. See Charge for usage |
Log line usage.report … failed |
The provider refused the usage. The job retries for about a day; the error says why |
409 This workspace already has a subscription |
Change plans in the customer portal, not by checking out again |
400 This workspace has no billing account yet |
The portal exists once someone has started a checkout |
| Log line saying a price is not one of your plans | Someone subscribed to a price made by hand in Stripe. Use the ones billing:sync made |
| Message or symptom | Fix |
|---|---|
email.send <id> failed (attempt 1 of 10): <reason> |
The mail server refused or was unreachable. It retries for about 2 hours |
Sent "<subject>" to <address> but nothing arrives |
Your provider accepted it. Check its logs, and that MAIL_FROM’s domain is verified |
| Nothing is sent at all | No worker is running. See Jobs below |
No email "<subject>" was sent to <address> in a test |
Nothing matching was sent. t.outbox.emails() shows what was |
Marketing site
Section titled “Marketing site”| Message or symptom | Fix |
|---|---|
The contact form is not set up. |
Set CONTACT_EMAIL on the API |
You have sent several messages already. Try again in an hour. |
5 messages an hour per address; wait, or restart the API while developing |
The contact form answers 502 |
The site cannot reach the API: set API_URL on the site, or run the API while developing |
| A new page shares with no image | Add it to pages in apps/site/src/lib/pages.ts |
| Message or symptom | Fix |
|---|---|
A job stays pending |
JOBS_WORKER=false and no bun run worker is running, or run_at is in the future |
No handler for the job "…" |
No @OnJob() for it in a provider that is part of a module |
Two handlers for the job "…". A job has exactly one. |
Two methods have @OnJob() for the same job |
… failed (attempt 5 of 5) as an error |
It gave up. The reason is in last_error on its row in jobs |
enqueue throws a ZodError |
The payload doesn’t match the job’s schema. Nothing was queued |
| A job ran twice | It ran over 10 minutes or a worker died. Make handlers safe to repeat |
| Message or symptom | Fix |
|---|---|
503 AI is not set up. Add AI_GATEWAY_API_KEY to .env. |
Set the key and restart the API |
The assistant could not answer. Try again in a moment. |
The gateway or model failed: wrong key, unknown model, no credit. The reason is in the API’s output |
| The answer arrives all at once | Something between the API and the browser buffers it |
Deploying
Section titled “Deploying”| Message or symptom | Fix |
|---|---|
There is no .env.railway yet. Create it with `bun run deploy:init`. |
Run bun run deploy:init |
RAILWAY_PROJECT is not set in .env.railway. |
Write your project’s name there |
RAILWAY_REPO is not set in .env.railway. |
This folder has no GitHub origin. Write owner/name there |
This folder is linked to the Railway project "…" |
The wrong project is linked. Run bun run railway link |
This version of railway/iac requires Railway CLI 5.42.1 or newer |
You ran your own older railway. Use bun run deploy:plan |
| Railway services are created, but never build | Install Railway’s GitHub app with access to your repository, then connect each service: bun run railway service source connect --repo <owner/name> --branch main --service <name> |
WEB_URL: Invalid URL in the API’s logs on Railway |
web has no address yet. Run bun run railway domain --service web --port 8080, then redeploy api |
| A push to your branch does not deploy | Connect each service as above; recording the repository in the file is not enough |
| Every log line on your host shows as info | Your host reads the level as a word. The kit writes "level":"warn" in production; a line with a number comes from something else |
There is no .env.aws yet. Create it with `bun run deploy:init aws`. |
Run bun run deploy:init aws |
Nothing was changed. Correct .env.aws, then run this again. |
A FAIL line above it says which setting. The same check runs alone as bun run deploy:check --before aws |
… is empty in .env.railway (or .env.aws), as a note |
Fine if it is already set on the host. If not, the API will not start: fill it in |
The migrations failed (exit 1), so nothing else was changed |
The version that was running still is. Read the migrations’ output in the CloudWatch stream the message names, fix the migration, and apply again |
Unable to resolve AWS account to use or ExpiredToken |
The AWS CLI is not signed in. Run aws login, then the command again |
ECS Service Linked Role is not ready |
The account had never used ECS. deploy:apply:aws now creates the role first; run it again |
AWS: CREATE_FAILED on the certificate, or it waits for an hour |
The domains are not in AWS_HOSTED_ZONE’s zone, or that zone is not the one the domain’s registrar points at |
| AWS: the deploy waits on a service for a long time, then rolls back | The new version never got healthy. Read its logs: aws logs tail <LogGroup> --log-stream-name-prefix api |
AWS: 502 under /api, and lookup api … no such host in the web app’s logs |
The web app started before the API, so it never learned the name. Fixed in the stack; on a stack deployed before the fix, run aws ecs update-service --force-new-deployment for web and site |
current credentials could not be used to assume …cdk-…-deploy-role… |
Harmless with the root account, which cannot assume roles: CDK uses your own credentials |
A FAIL line from deploy:check |
It names what failed. Check the container’s logs |
| Links in emails point at the wrong address | WEB_URL is wrong, or the API started before web had an address: redeploy api |