TypebaseTypebase

Troubleshooting

Solutions for the most common issues you might run into with Typebase.

Last updated on

This page covers the issues users hit most often, grouped by the area they show up in. If you don't find your problem here, the underlying libraries are well-documented. Start with oRPC, Drizzle, or better-auth depending on which layer is failing.

Installation

Peer dependency warning about drizzle-orm

When you install Typebase you may see a warning like this:

 WARN  Issues with peer dependencies found
.
└─┬ typebase-io-cli 0.0.1
  ├─┬ @better-auth/drizzle-adapter 1.6.8
  │ └── ✕ unmet peer drizzle-orm@^0.45.2: found 1.0.0-beta.22
  └─┬ better-auth 1.6.8
    └── ✕ unmet peer drizzle-orm@^0.45.2: found 1.0.0-beta.22

Typebase pins drizzle-orm to the 1.0.0-beta line to get the newer query builder, while better-auth and @better-auth/drizzle-adapter still declare ^0.45.2 as their peer range. The APIs Typebase relies on are compatible, so this is safe to ignore. The warning is just the package manager noticing the version mismatch on paper.

Most package managers only warn and continue:

  • npm (v7+): warns, installs everything.
  • yarn classic (v1): warns, installs everything.
  • bun: warns, installs everything.

pnpm is the exception: with strict-peer-dependencies=true (the default in some setups), it will refuse to install. If that happens, add this to the root of your project:

.npmrc
strict-peer-dependencies=false

Then re-run pnpm install.

yarn berry (v2+) installs everything too, but under PnP the missing peer can turn into a runtime resolution error when better-auth tries to import drizzle-orm. The cleanest fix is to widen the peer range via packageExtensions in .yarnrc.yml:

.yarnrc.yml
packageExtensions:
  'better-auth@*':
    peerDependencies:
      drizzle-orm: '*'
  '@better-auth/drizzle-adapter@*':
    peerDependencies:
      drizzle-orm: '*'

Then run yarn install again. Once better-auth bumps its peer range to the Drizzle 1.0 line, none of these workarounds will be needed.

esbuild binary is missing / "you installed esbuild for another platform"

The Typebase CLI uses esbuild to transpile your server before deploying. esbuild ships a native binary and runs a post-install script to place the right one for your platform. Some package managers block post-install scripts by default, so the binary never lands and the CLI errors out the first time you run npx typebase-io-cli deploy or npx typebase-io-cli generate-server.

pnpm (v9+) is the most common case. It prints something like:

╭ Warning ─────────────────────────────────────────────────────────────
│   Ignored build scripts: esbuild.
│   Run "pnpm approve-builds" to pick which dependencies should be allowed to run scripts.
╰──────────────────────────────────────────────────────────────────────

Approve the build once and pnpm will remember it for future installs:

pnpm approve-builds

Pick esbuild from the list. To do it without the prompt, add it to package.json:

package.json
{
  "pnpm": {
    "onlyBuiltDependencies": ["esbuild"]
  }
}

Then re-run pnpm install so the binary downloads.

yarn berry blocks build scripts by default too. Allow esbuild in .yarnrc.yml:

.yarnrc.yml
enableScripts: true

Or, more scoped, use supportedArchitectures / packageExtensions to whitelist esbuild specifically. See the Yarn docs for the narrower options.

bun allows build scripts by default, but if you've locked them down with trustedDependencies, add esbuild:

package.json
{
  "trustedDependencies": ["esbuild"]
}

npm runs post-install scripts by default, so this usually only comes up if you've passed --ignore-scripts.

Codegen

Client types are out of date or missing

The _generated/ folder is a snapshot taken the last time you ran npx typebase-io-cli codegen. Adding, removing, or renaming any of the following requires a fresh run:

  • A file inside actions/
  • auth.ts
  • db/schema.ts or db/relations.ts
npx typebase-io-cli codegen

You don't need to re-run codegen when you only change the contents of an existing file. The action body, schema columns, or auth options will pick up automatically the next time you build.

Codegen fails with "cannot find module" or path errors

This usually means projectPath in typebase.json doesn't match where your typebase/ directory actually lives. Check the Configuration page and confirm projectPath is correct relative to your project root.

db is missing from the handler context

Two things to check:

  1. You have a typebase/db/schema.ts file. The db client is only injected when one exists.
  2. You ran npx typebase-io-cli codegen after creating it. The action builder is regenerated based on what files exist.

The same applies to auth. It requires a typebase/auth.ts file plus a fresh codegen run.

Deployment

"Schema changes are destructive" prompt I didn't expect

The CLI flags any change that drops a table, drops a column, or renames a column. If you're seeing this when you didn't intend to lose data, it usually means a schema edit was unintentional. For example, you renamed a column without telling Drizzle (Drizzle sees this as a drop + add). Cancel the prompt, restore the old name, and run npx typebase-io-cli deploy again.

Deploy succeeds but my server can't reach the database

The CLI sets DATABASE_URL on your provider on the first deploy. If you've rotated the Neon connection string or moved branches manually, the value on your provider may be stale. Check the current value with dev or prod depending on the environment that's failing:

npx typebase-io-cli env dev get DATABASE_URL
npx typebase-io-cli env prod get DATABASE_URL

Then replace it with the new one, against the same target:

npx typebase-io-cli env dev add DATABASE_URL <new-value>
npx typebase-io-cli env prod add DATABASE_URL <new-value>

If env get prints ENCRYPTED, the provider is hiding the raw secret. In that case, check or replace the value from the original source instead, usually your provider dashboard or Neon project settings.

Deploy fails partway through

npx typebase-io-cli deploy runs several steps in sequence. If it fails between steps, the partial state is usually safe to retry. The CLI is idempotent for the early steps (codegen, generate-server, transpile) and the destructive ones (schema push, deploy) are gated on the previous step succeeding. Re-run the same command.

If it keeps failing on the same step, run that step in isolation to see the underlying error:

  • Schema push errors → npx typebase-io-cli db dev push
  • Auth/secret errors → npx typebase-io-cli env dev get BETTER_AUTH_SECRET (some providers return ENCRYPTED instead of the raw value)
  • Provider errors → log into your provider's dashboard and check the deployment log

Provider authentication expired

Vercel, Cloudflare, and Deno all use short-lived tokens. If you suddenly get 401s during deploy, log in again. The CLI will prompt you. There's no separate "logout" step; just re-run npx typebase-io-cli deploy and authenticate when asked.

Deno Deploy returns "502 Server Error"

When deploying to Deno Deploy you may occasionally see an HTML response like this:

<html>
  <head>
    <title>502 Server Error</title>
  </head>
  <body>
    <h1>Error: Server Error</h1>
    <h2>
      The server encountered a temporary error and could not complete your request.
      <p>Please try again in 30 seconds.</p>
    </h2>
  </body>
</html>

This is returned by Deno Deploy's API gateway, typically when you've hit a rate limit on their side. Wait ~30 seconds and re-run the command. Most of the time the next attempt goes through.

If you're hitting this repeatedly and it's blocking your workflow, the simplest fix is to switch to another provider. Typebase also supports Vercel and Cloudflare Workers. Point your typebase.json at one of them and re-deploy.

Cloudflare Workers: "Worker exceeded CPU time limit."

When calling a deployed route on Cloudflare Workers you may see a response like:

{
  "message": "Worker exceeded CPU time limit.",
  "outcome": "exceededCpu",
  "statusCode": 503
}

Cloudflare enforces a hard CPU-time budget per request, separate from wall-clock time. The Workers Free plan caps requests at 10ms of CPU time, and the Paid plan at 30s. The error is returned when your handler burns more CPU than the cap, regardless of how fast the I/O around it was.

You'll find the actual CPU time used per request in the Cloudflare dashboard under Workers & Pages → your worker → Observability → Logs, or via npx wrangler tail <worker-name>.

The three usual fixes, in order of how aggressive you want to be:

1. Upgrade your Workers plan

The Free plan's 10ms limit is genuinely impractical for anything CPU-heavy. The Paid plan ($5/month at the time of writing) raises the limit to 30s, which fits any normal workload. If you're shipping a real app on Cloudflare, this is the expected baseline.

2. Check if your action is doing CPU-intensive work

If you're on the Paid plan and still hitting the limit, your handler is doing something expensive on the hot path. Common culprits:

  • Large JSON parses or stringifies
  • Big in-memory loops over collections
  • Synchronous compression/encryption
  • Heavy template or markdown rendering

Move the expensive part off the request path (queue + Durable Object + scheduled job) or do it on the client.

3. Replace the auth password hasher with Node's scrypt

The most common cause of this error is password hashing during sign-up / sign-in. better-auth's default scrypt implementation is pure JavaScript, which burns a lot of CPU time on Workers. Node's built-in node:crypto.scryptSync is implemented in native code via OpenSSL and runs orders of magnitude faster, fast enough to fit comfortably under the Workers Paid limit (and sometimes even under the Free limit).

Cloudflare Workers expose node:crypto via the Node.js compatibility flag, which Typebase enables by default for Cloudflare deployments.

Override the hasher in your auth.ts:

typebase/auth.ts
import { defineAuth } from 'typebase-io/server';
import { scryptSync, randomBytes, timingSafeEqual } from 'node:crypto';

export const auth = defineAuth({
  // ...
  emailAndPassword: {
    enabled: true,
    password: {
      async hash(password: string) {
        const salt = randomBytes(16).toString('hex');
        const derivedKey = scryptSync(password, salt, 64);
        return `${salt}:${derivedKey.toString('hex')}`;
      },
      async verify({ hash, password }) {
        const [salt, key] = hash.split(':');
        const derivedKey = scryptSync(password, salt, 64);
        return timingSafeEqual(Buffer.from(key, 'hex'), derivedKey);
      },
    },
  },
});

Note that swapping the hasher means existing users hashed with the old implementation won't be able to log in until they reset their password, so do this before you have real users.

Database

"Relation does not exist" when querying

You added a table to schema.ts but haven't pushed the schema to the database branch you're querying. Push to the target that's failing. dev is the common case, but the same issue happens on prod if you deploy code that references a table you never pushed there:

npx typebase-io-cli db dev push
npx typebase-io-cli db prod push

Or just deploy: npx typebase-io-cli deploy dev / npx typebase-io-cli deploy prod runs the push automatically for each target.

"No relations found" when using db.query.X

Every table exported from schema.ts must also appear in db/relations.ts, even if it has no foreign keys. See Relations for the empty-entry pattern.

Schema push hangs or times out

Neon free-tier branches sleep when idle and take a few seconds to wake up. The first push after a long gap can be slow. If it hangs for more than ~30 seconds, cancel and retry. The second attempt will hit the awake branch.

Auth

auth generate says "no schema file found"

You need both typebase/auth.ts and typebase/db/schema.ts before running auth generate. The command appends auth tables to your existing schema; it won't create one from scratch.

Sessions don't persist on the frontend

The most common cause is a missing or misconfigured proxy. Auth cookies must be set on your frontend's domain, not on your Typebase server's domain. See the proxy setup section in your framework's integration guide (Next.js, SvelteKit, Nuxt).

If you're using Expo, sessions are stored in SecureStore instead of cookies. Make sure the expoClient plugin is wired up on the client and the expo() plugin is in your auth.ts.

"Origin not trusted" errors

Add the exact origin to trustedOrigins in your auth.ts:

trustedOrigins: ['http://localhost:3000', 'https://my-app.vercel.app'];

For Expo dev builds you'll also need scheme entries like myapp:// and exp://**. See the Expo guide for the full list.

Local development

generate-server output is stale

The generated server in typebase/_server/ is a snapshot. It doesn't watch your files. Re-run npx typebase-io-cli generate-server and restart the local server every time you change anything inside typebase/. If you find yourself doing this constantly, deploying to dev is usually the smoother loop. See Work locally.

Local server starts but my frontend can't reach it

Three things to check:

  1. TYPEBASE_APP_URL in your frontend's .env points to http://localhost:8080 (or whichever port your local server uses).
  2. If you have an auth.ts, your frontend's local origin is in trustedOrigins.
  3. Your local server actually has a working DATABASE_URL and BETTER_AUTH_SECRET in typebase/_server/.env. Note that the generated server reads a plain DATABASE_URL, while the CLI writes two separate values into your project-root .env (DATABASE_URL for the prod branch and DATABASE_URL_DEV for the dev branch) so they can coexist. Copy whichever branch you want to run against into typebase/_server/.env as DATABASE_URL. If the value is missing from your project root, fetch it from the provider with npx typebase-io-cli env dev get DATABASE_URL or npx typebase-io-cli env prod get DATABASE_URL. If that returns ENCRYPTED, grab the value from the Neon console instead.

Still stuck?

If none of the above helps, please open an issue on GitHub with:

  • What you ran
  • The full error output
  • Your typebase.json (with secrets redacted)
  • Your framework and Node version

We'll triage and either fix the underlying issue or add it to this page.

On this page