Troubleshooting
The failures we actually hit, and what causes each one. If you hit something that is not here, please add it.
Setup
bun run verify fails with connection errors
You skipped bun run db:test-setup. Each package tests against its own database, and without them the suite cannot connect. The errors look like broken tests rather than missing setup, which is what makes this one expensive.
bun run infra:up
bun run db:test-setupbun: command not found
curl -fsSL https://bun.sh/install | bashThen open a new shell. Orbit needs 1.3 or newer, and bun --version will tell you what you have.
Docker containers will not start
docker ps -a
docker logs orbit-postgresUsually a port is already taken. Postgres wants 5434, Redis 6380, MinIO 9010 and 9011. If a previous run left a volume in a bad state:
bun run infra:reset
bun run db:push && bun run db:test-setup && bun run db:seedinfra:reset destroys the data. That is fine locally, and never run it against anything you care about.
Port already in use
lsof -i :3000A previous bun run dev that did not shut down cleanly is the usual cause. It can also be a dev server from another worktree, which is easy to miss because the app answers but serves a different checkout.
A module cannot be resolved, but it is in package.json
The workspace symlinks are out of date. This happens after switching branches that changed dependencies.
bun installIf that does not do it, delete node_modules and install again. The symptom is a Module not found for a package that is clearly declared, and the dev server returns 500 on every route while looking otherwise healthy.
Signing in
The login screen shows no users
Either ORBIT_DEV_LOGIN=1 is missing from .env, or you have not seeded:
bun run db:seedSign-in loops back to the login screen
BETTER_AUTH_URL does not exactly match the origin you are visiting. If you are on http://localhost:3000 it has to be exactly that, not 127.0.0.1, not a trailing slash, not https.
Sign-in codes and invites never arrive
EMAIL_FROM is not on a domain verified in Resend, or RESEND_API_KEY is missing. Every send fails silently from the user's point of view.
Locally, use ORBIT_DEV_LOGIN=1 instead of setting up email at all.
A user cannot join
ALLOWED_EMAIL_DOMAINS is set and their domain is not in it. It is enforced on both invite creation and user creation. The workspace may also have its own narrower allowedEmailDomains.
Realtime
An endless "Reconnecting to live updates" banner
The expected behavior depends on the deployment route.
On Vercel: inspect the websocket request to /api/ws and verify that it receives a 101 Switching Protocols response. If it does not, continue with The websocket never reaches a 101. NEXT_PUBLIC_REALTIME_URL cannot redirect a production socket because Orbit ignores it whenever NODE_ENV is production.
With standalone Node (Preview): realtime is not supported yet because /api/ws depends on Vercel's websocket request context. Reverse proxy settings cannot enable it. Use Vercel when realtime is required, and see Self-hosting for the current standalone limitations.
Locally: the realtime server is not running. bun run dev starts it. NEXT_PUBLIC_REALTIME_URL should be ws://localhost:3100.
The websocket never reaches a 101
bunVersion is set in apps/web/vercel.json, so functions run on the Bun runtime where experimental_upgradeWebSocket silently never fires. Remove it.
It can also happen if something in the route awaits Redis or the database before upgrading. Upgrade first, attach afterwards, and buffer whatever arrives in between.
Changes do not appear in other tabs, and there is no banner
The socket is connected but nothing is being published, so Redis is the problem. Check REDIS_URL and that Redis is reachable from where the app runs.
redis-cli -u "$REDIS_URL" pingSome people see an update and others do not
A scope problem, and this one is a security bug rather than a sync bug, because the reverse case means someone is being delivered a row they may not read.
Scopes have to match who may read the row. A project and its milestones carry the scopes of the teams that own them, falling back to the workspace scope only when the project belongs to no team. A private saved view carries its owner alone. See Architecture.
A user stays connected after signing out
Should not happen. Signing out publishes a revocation and the hub closes the connection, and the hub also sweeps sessions on an interval as a backstop. If you see it, that is worth reporting.
Database
bun run db:push hangs or times out
Against a hosted database, you are probably using the pooled connection string. Schema changes need a direct session. Use the provider's direct endpoint rather than its transaction-pooled runtime endpoint.
Connection pool exhausted in production
DATABASE_URL points at the direct endpoint instead of the pooler. Serverless functions open many short lived connections and the direct endpoint runs out. Swap it for the pooled string.
Prepared statement errors in production
Set DATABASE_PREPARED_STATEMENTS=false and remove any prepare query option from DATABASE_URL. Only set the variable to true when the endpoint supports protocol-level named prepared statements. Pooling mode and port number alone do not establish that capability.
Tests deadlock or hit foreign key violations for no reason
Two test runs are sharing a database, which happens with two worktrees or two agents. Set ORBIT_TEST_LANE to something unique in each:
ORBIT_TEST_LANE=my-branch bun run test
ORBIT_TEST_LANE=my-branch bun run db:test-lanes-dropThe cleanup drops that lane alone, so it cannot delete a lane another run is using. Without ORBIT_TEST_LANE it refuses. --all drops every lane on that Postgres and is the only mode that can touch someone else's.
The demo data is a mess
bun run db:seedIt truncates and reloads. Use it only with a verified demo database, and never point it at production. The exact default local target needs no confirmation; every other verified target requires the exact confirmation described below.
Only the exact default Docker Compose target, postgres://orbit:orbit@localhost:5434/orbit or the same target through 127.0.0.1, needs no confirmation. Every other target, including other localhost and IPv4 loopback ports, databases, or credentials, plus postgres and host.docker.internal, must have an explicit username and port in DATABASE_URL. For these non-default targets, db:seed stops unless ORBIT_SEED_CONFIRM_TARGET exactly matches the credential-safe host:port/database:user-sha256:<64 lowercase hex characters> target printed in the error. The username is decoded before hashing and is never printed. For example:
ORBIT_SEED_CONFIRM_TARGET='db.example.com:5432/orbit:user-sha256:<64-character-sha256>' bun run db:seedMulti-host URLs, ambiguous encoded authorities, and query options that can change the target database or schema are refused. IPv6 connection URLs are also refused because the current database driver misparses bracketed IPv6 hosts. Use a hostname or IPv4 endpoint instead.
Files
Uploads fail in the browser, the server looks fine
The bucket CORS policy does not allow your origin. Uploads go straight from the browser through a presigned PUT, so the server never sees the failure.
sed 's|__ORBIT_ORIGIN__|https://orbit.example.com|' infra/s3-cors.json > /tmp/cors.json
aws s3api put-bucket-cors --bucket "$S3_BUCKET" --cors-configuration file:///tmp/cors.jsonUploads fail locally
MinIO is not running, or the bucket was not created. bun run infra:up does both. The console is on http://localhost:9011 with orbitminio and orbitminio.
Building and CI
bun run check-comments fails
You added a comment. The policy allows only functional directives such as @ts-expect-error and biome-ignore. Put the meaning in names and structure, and put prose in the pull request or in docs/.
bun run check-commentsnames the file and line.
bun run check-deps fails after a dependency bump
A package listed in the root overrides block resolved to two versions, or a manifest asked for a version the override will not let it have. Bun keeps a transitive resolution that still satisfies its own range, so a bump applied to one manifest alone leaves the previous copy nested under everything else that depends on it. Two copies of a library whose types cross module boundaries, any CodeMirror package above all, fail typecheck with TS2375 on exactOptionalPropertyTypes, and a facet or an instance compared across the two copies fails at runtime as soon as the code reaches for one.
An override also shadows a direct dependency, which is why the second failure exists: without it a bump could sit in package.json and install nothing. Move both, then install.
bun pm why @codemirror/view
bun install
bun run check-depsTypecheck fails on something that looks fine
noUncheckedIndexedAccess and exactOptionalPropertyTypes are on, so array[0] is T | undefined and an optional property cannot be set to undefined explicitly. Both catch real bugs. Handle the undefined rather than asserting it away, since non-null assertions are a lint error.
The build fails but bun run dev works
Almost always a Bun built-in in shipped server code. The deployed runtime is node, so import ... from 'bun' fails there. See the table in Architecture.
Test files and apps/realtime are exempt. packages/realtime-server is not, because the web app imports it.
E2E passes locally and fails in CI
Download the Playwright trace artifact from the failed run:
bunx playwright show-trace trace.zipIt shows every action, the DOM at each step, and the network. Usually a timing assumption that holds on a fast machine and not on a CI runner.
Still stuck
- Discussions for questions.
- Issues for a bug.
Include the output of bun --version, your OS, whether it is local, self-hosted or hosted Orbit, and the actual error. Those four make the difference between a fix this week and a thread that goes nowhere.