Turso error
LibsqlError: SERVER_ERROR: Server returned HTTP status 401
"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
- 01Find 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 . - 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.
- 03Create 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 - 04Replace the value in Vercel with the new token, pasting only the token, for each environment that runs the app.
- 05Redeploy. Vercel applies changed variables only to new deployments. Then load the failing route: the function log should no longer show the 401.
Related
Related guides
Sources
Official docs behind this checklist
- Turso, API response codes (401 Unauthorized)
- Turso CLI, db tokens create (--expiration, --read-only)
- Turso CLI, db show (--url)
- Turso, Vercel integration (variables, @libsql/client/web)
- Turso, TypeScript quickstart (TURSO_DATABASE_URL, TURSO_AUTH_TOKEN)
- Vercel, Environment variables
- hrana-client-ts, http/stream.ts (the status message)
- libsql-client-ts, libsql-client/src/http.ts
- libsql-client-ts, libsql-client/src/web.ts
- libsql-client-ts, libsql-core/src/config.ts
- Vercel Community, Vercel Turso integration is returning 401 status code
Browse all launch guides · Read resources · How to make and check your next change
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.