tursodatabase / tursodatabase/libsql-client-ts

Vercel Turso integration returning 401 for `@libsql/client`

Open
#280 6 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
576
Forks
69
PR merge metrics
No merged PRs in 30d

Description

Description

Hi!

When accessing data from my site hosted on Vercel, I'm getting a 401 HTTP status code being 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",
});

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with app/db/index.ts and compare the deployed Vercel environment with the local values used by drizzle.config.ts. Run the database query from app/routes/temp._index/route.tsx in both environments and trace the @libsql/client request. Done means the deployed query is authorized without a 401 and the cause is documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.