All guidesMore providers: databases, auth, hosting, email and payments
On this page

Turso error

Turso

LibsqlError: SERVER_ERROR: Server returned HTTP status 401

By Ohad Krispin, founder of VibeRaven · LinkedIn

"LibsqlError: SERVER_ERROR: Server returned HTTP status 401" means the Turso server answered a request from @libsql/client with HTTP 401, so it did not accept the credentials the client sent. The only credential the client sends is the authToken option, as a Bearer header, and it sends no header when TURSO_AUTH_TOKEN is undefined, so a missing, a wrong and an expired token all print the same message. Turso's API docs describe a 401 as an invalid or expired auth token. Create a new token with turso db tokens create <database-name>, set it as TURSO_AUTH_TOKEN in Vercel for the environment that shows the error, and redeploy, because Vercel applies changed variables only to new deployments. Reproduced with @libsql/client 0.18.0 on 2026-10-31.

What the error looks like, and where the text comes from

The client turns every non-2xx answer into a LibsqlError with the code SERVER_ERROR. The message is built from the status code alone: hrana-client-ts writes "Server returned HTTP status" followed by the number, and when the server sends a text/plain body it adds the body after a colon. In @libsql/client 0.18.0 a libsql: URL is opened over https by default, so the 401 comes back on a normal HTTP response. In our run the server was a small local HTTP server that answers every request with 401, not a Turso database; it shows the message the client builds, not what a Turso database sends in the body.

LibsqlError: SERVER_ERROR: Server returned HTTP status 401
    code: 'SERVER_ERROR'

// if the server also sends a text body, it follows a colon:
// SERVER_ERROR: Server returned HTTP status 401: <body>

Is it a missing TURSO_AUTH_TOKEN or a wrong one?

The message cannot tell you. Against the local server, a client created with authToken undefined sent a request with no Authorization header, and a client created with a made-up token sent "Authorization: Bearer" followed by the token. Both threw the same error. So check from your own code, and log whether the variable is set, never its value.

console.log('TURSO_DATABASE_URL set:', Boolean(process.env.TURSO_DATABASE_URL))
console.log('TURSO_AUTH_TOKEN set:', Boolean(process.env.TURSO_AUTH_TOKEN))

It works locally and fails only in the Vercel function

A report in the Vercel Community on 2024-10-27 shows this exact error from @libsql/client 0.14.0 in Vercel's serverless runtime only, with migrations using the same credentials passing during the build; the thread records no resolution. Turso's Vercel page lists TURSO_DATABASE_URL and TURSO_AUTH_TOKEN as the variables to configure and says to import @libsql/client/web instead of @libsql/client for Vercel deployments. Both entry points build the same HTTP client and pass the token the same way (libsql-client-ts, web.ts and http.ts), so switching the import does not fix a bad token. Vercel scopes each variable to Production, Preview or both, so a token that exists only for Production fails on a preview deployment.

Checklist

How to fix it, then check the fix

  1. 01
    Find where the client is created and which variables it reads, including committed .env files:
    grep -rnE "TURSO_AUTH_TOKEN|TURSO_DATABASE_URL|authToken" --exclude-dir=node_modules --exclude-dir=.next .
  2. 02In the Vercel project's Environment Variables settings, check that TURSO_AUTH_TOKEN and TURSO_DATABASE_URL exist for the environment that served the error, Production or Preview.
  3. 03
    Create a new token for that database. The Turso docs say --expiration takes never or a number of days, and they do not state a default, so choose one on purpose. Check the URL the same way:
    turso db tokens create <database-name> --expiration never
    turso db show <database-name> --url
  4. 04Replace the value in Vercel with the new token, pasting only the token, for each environment that runs the app.
  5. 05Redeploy. Vercel applies changed variables only to new deployments. Then load the failing route: the function log should no longer show the 401.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Does an expired token print a different message?

Not in the client code. The message carries the status code and, for a text/plain answer, the body. Turso's API docs list 401 as an invalid or expired auth token, so treat a wrong token and an expired one the same way: make a new one.

Should I import @libsql/client/web on Vercel?

Turso's Vercel page says to use @libsql/client/web instead of @libsql/client for Vercel deployments. That choice is about the runtime. It does not change how the token is sent, so a 401 needs a valid token either way.