Vercel Turso integration is returning 401 status code

Description

Hi!

I’ve set up a new site and would like to use Turso as my database provider. However, when I try accessing data from my site hosted on Vercel, I’m getting a 401 HTTP status code thrown from the @libsql/client and I have no clue why. When I connect to the database locally, I simply cannot reproduce the issue.

I tried recreating the database, recreating the group, and tweaking the Drizzle configuration, but none of it seemed to work. I have no idea what’s throwing this exception and why the request from Vercel isn’t getting authorized properly.

If more information is necessary, I’d be more than happy to provide this!

Thanks in advance!


More details

Stacktrace
LibsqlError: SERVER_ERROR: Server returned HTTP status 401
    at mapHranaError (file:///var/task/node_modules/.pnpm/@libsql+client@0.14.0/node_modules/@libsql/client/lib-esm/hrana.js:268:16)
    at file:///var/task/node_modules/.pnpm/@libsql+client@0.14.0/node_modules/@libsql/client/lib-esm/http.js:76:23
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async LibSQLPreparedQuery.get (file:///var/task/node_modules/.pnpm/drizzle-orm@0.35.3_@libsql+client-wasm@0.14.0_@libsql+client@0.14.0_react@19.0.0-rc-cae764ce-_beykhe332nz34cv7xpk57h4rsy/node_modules/drizzle-orm/libsql/session.js:155:18)
    at async login (file:///var/task/build/server/nodejs-eyJydW50aW1lIjoibm9kZWpzIn0/index.js:505:17)
    at async file:///var/task/build/server/nodejs-eyJydW50aW1lIjoibm9kZWpzIn0/index.js:1955:20
    at async Object.callRouteAction (/var/task/node_modules/.pnpm/@remix-run+server-runtime@2.13.1_typescript@5.6.3/node_modules/@remix-run/server-runtime/dist/data.js:36:16)
    at async /var/task/node_modules/.pnpm/@remix-run+router@1.20.0/node_modules/@remix-run/router/dist/router.cjs.js:4719:19
    at async callLoaderOrAction (/var/task/node_modules/.pnpm/@remix-run+router@1.20.0/node_modules/@remix-run/router/dist/router.cjs.js:4785:16)
    at async Promise.all (index 2) {
  code: 'SERVER_ERROR',
  rawCode: undefined,
  [cause]: HttpServerError: Server returned HTTP status 401
      at errorFromResponse (file:///var/task/node_modules/.pnpm/@libsql+hrana-client@0.7.0/node_modules/@libsql/hrana-client/lib-esm/http/stream.js:352:16)
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
    status: 401
  }
}
package.json
{
	"name": "[REDACTED]",
	"private": true,
	"sideEffects": false,
	"type": "module",
	"scripts": {
		"build": "remix vite:build",
		"dev": "run-p dev:*",
		"dev:app": "remix vite:dev",
		"dev:turso": "turso dev --db-file './drizzle/db/data.db'",
		"dev:drizzle": "drizzle-kit studio",
		"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
		"start": "remix-serve ./build/server/index.js",
		"db:push": "drizzle-kit push",
		"db:generate": "drizzle-kit generate",
		"db:migrate": "dotenv -e .env -- tsx ./drizzle/migrate.ts",
		"db:seed": "dotenv -e .env -- tsx ./drizzle/seed.ts",
		"db:studio": "drizzle-kit studio",
		"typecheck": "tsc",
		"format": "prettier --check --cache .",
		"prepare": "husky"
	},
	"dependencies": {
		"@conform-to/react": "^1.2.2",
		"@conform-to/zod": "^1.2.2",
		"@epic-web/invariant": "^1.0.0",
		"@epic-web/totp": "^2.0.0",
		"@libsql/client": "^0.14.0",
		"@paralleldrive/cuid2": "^2.2.2",
		"@radix-ui/react-dialog": "^1.1.2",
		"@radix-ui/react-dropdown-menu": "^2.1.2",
		"@radix-ui/react-label": "^2.1.0",
		"@radix-ui/react-popover": "^1.1.2",
		"@radix-ui/react-select": "^2.1.2",
		"@radix-ui/react-separator": "^1.1.0",
		"@radix-ui/react-slot": "^1.1.0",
		"@radix-ui/react-tooltip": "^1.1.3",
		"@react-email/components": "^0.0.25",
		"@remix-run/node": "^2.13.1",
		"@remix-run/react": "^2.13.1",
		"@remix-run/router": "^1.20.0",
		"@remix-run/serve": "^2.13.1",
		"@t3-oss/env-core": "^0.11.1",
		"@vercel/remix": "^2.13.1",
		"bcryptjs": "^2.4.3",
		"class-variance-authority": "^0.7.0",
		"clsx": "^2.1.1",
		"cmdk": "^1.0.0",
		"drizzle-orm": "^0.35.3",
		"input-otp": "^1.2.4",
		"isbot": "^5.1.17",
		"lucide-react": "^0.453.0",
		"react": "rc",
		"react-dom": "rc",
		"remix-themes": "^1.5.1",
		"resend": "^4.0.0",
		"sonner": "^1.5.0",
		"spin-delay": "^2.0.1",
		"tailwind-merge": "^2.5.4",
		"tailwindcss-animate": "^1.0.7",
		"zod": "^3.23.8"
	},
	"devDependencies": {
		"@eslint/js": "^9.13.0",
		"@faker-js/faker": "^9.0.3",
		"@remix-run/dev": "^2.13.1",
		"@total-typescript/tsconfig": "^1.0.4",
		"@types/bcryptjs": "^2.4.6",
		"@types/react": "npm:types-react@rc",
		"@types/react-dom": "npm:types-react-dom@rc",
		"autoprefixer": "^10.4.20",
		"dotenv-cli": "^7.4.2",
		"drizzle-kit": "^0.26.2",
		"eslint": "^9.13.0",
		"eslint-config-prettier": "^9.1.0",
		"eslint-import-resolver-typescript": "^3.6.3",
		"eslint-plugin-import-x": "^4.3.1",
		"eslint-plugin-jsx-a11y": "^6.10.2",
		"eslint-plugin-perfectionist": "^3.9.1",
		"eslint-plugin-react": "^7.37.2",
		"eslint-plugin-react-hooks": "^5.0.0",
		"eslint-plugin-react-refresh": "^0.4.14",
		"globals": "^15.11.0",
		"husky": "^9.1.6",
		"lint-staged": "^15.2.10",
		"npm-run-all": "^4.1.5",
		"postcss": "^8.4.47",
		"prettier": "^3.3.3",
		"prettier-plugin-tailwindcss": "^0.6.8",
		"tailwindcss": "^3.4.14",
		"tsx": "^4.19.1",
		"typescript": "^5.6.3",
		"typescript-eslint": "^8.11.0",
		"vite": "^5.4.10",
		"vite-tsconfig-paths": "^5.0.1"
	},
	"overrides": {
		"@types/react": "npm:types-react@rc",
		"@types/react-dom": "npm:types-react-dom@rc"
	},
	"engines": {
		"node": ">=20.0.0"
	},
	"packageManager": "pnpm@9.12.2",
	"lint-staged": {
		"*": "prettier --write --ignore-unknown"
	}
}
app/db/index.ts
import { createClient } from "@libsql/client";
import { drizzle } from "drizzle-orm/libsql";

import { env } from "~/lib/env.server";

import * as schema from "./schema";

export const connection = createClient({
	authToken: env.TURSO_AUTH_TOKEN,
	url: env.TURSO_DATABASE_URL,
});

export const db = drizzle(connection, { schema });
app/routes/temp._index/route.tsx
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

import { db } from "~/db";

export async function loader() {
	const users = await db.query.user.findMany();

	return json({ users });
}

export default function Route() {
	const data = useLoaderData<typeof loader>();

	return (
		<div>
			<h1>Users</h1>
			<pre>{JSON.stringify(data, null, 2)}</pre>
		</div>
	);
}
drizzle.config.ts
import { defineConfig } from "drizzle-kit";

export default defineConfig({
	dbCredentials: {
		authToken: process.env.TURSO_AUTH_TOKEN || undefined,
		// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
		url: process.env.TURSO_DATABASE_URL!,
	},
	dialect: "turso",
	out: "./drizzle/migrations",
	schema: "./app/db/schema.ts",
});

Hi,

You might need to add driver: "turso" in your config file and make sure all env variables are set correctly and loaded when running commands.

Hi,

I’ve done exactly that and even tried only using drizzle() to create a DB instance:

export const db = drizzle({
	connection: {
		authToken: env.TURSO_AUTH_TOKEN,
		url: env.TURSO_DATABASE_URL,
	},
	schema,
});

However, this also doesn’t seem to work. I’ve even logged the TURSO_AUTH_TOKEN and TURSO_DATABASE_URL above the function call. The token and URL both are logged during the build and on runtime when I access a page that’s using the database.

The token and connection string are correct as I’m using the same token and connection string to migrate the database when the site gets built. This is working as expected, so I really have no idea why I’m not able to connect to the database during runtime.

I’ve created a reproduction application which is simply the Remix Vercel template with a Turso Drizzle integration following the steps from the Drizzle documentation

This is giving the same error as mentioned before. What is going on here?

I figured out that this is only involving the serverless environment. The Turso connection is working fine on the edge environment:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.