(on middleware) [Better Auth]: INTERNAL_SERVER_ERROR Error: Failed query
2025-08-10T13:39:59.581Z ERROR [Better Auth]: INTERNAL_SERVER_ERROR Error: Failed query: select "id", "expires_at", "token", "created_at", "updated_at", "ip_address", "user_agent", "user_id", "impersonated_by" from "session" where "session"."token" = $1
params: 7lIiWUkRjrVmOh4UJ0rZgjrvITaKu3ve
2025-08-10T13:39:59.581Z ERROR [Better Auth]: INTERNAL_SERVER_ERROR Error: Failed query: select "id", "expires_at", "token", "created_at", "updated_at", "ip_address", "user_agent", "user_id", "impersonated_by" from "session" where "session"."token" = $1
params: 7lIiWUkRjrVmOh4UJ0rZgjrvITaKu3ve
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Skip setup check for setup page and API routes
if (pathname.startsWith("/setup") || pathname.startsWith("/api/setup")) {
return NextResponse.next();
}
// Check if setup is needed (except for setup-related routes)
try {
const setupResponse = await fetch(new URL("/api/setup/check", request.url));
if (setupResponse.ok) {
const setupData = await setupResponse.json();
if (setupData.needsSetup) {
return NextResponse.redirect(new URL("/setup", request.url));
}
}
} catch (error) {
console.error("Setup check error:", error);
}
// Only protect admin routes
if (pathname.startsWith("/admin")) {
try {
const session = await auth.api.getSession({
headers: request.headers,
});
if (!session?.user) {
return NextResponse.redirect(new URL("/login", request.url));
}
// Check if user has admin/staff role
if (
!session.user.role ||
!["admin", "staff"].includes(session.user.role)
) {
return NextResponse.redirect(new URL("/", request.url));
}
} catch (error) {
console.error("Middleware auth error:", error);
return NextResponse.redirect(new URL("/login", request.url));
}
}
return NextResponse.next();
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Skip setup check for setup page and API routes
if (pathname.startsWith("/setup") || pathname.startsWith("/api/setup")) {
return NextResponse.next();
}
// Check if setup is needed (except for setup-related routes)
try {
const setupResponse = await fetch(new URL("/api/setup/check", request.url));
if (setupResponse.ok) {
const setupData = await setupResponse.json();
if (setupData.needsSetup) {
return NextResponse.redirect(new URL("/setup", request.url));
}
}
} catch (error) {
console.error("Setup check error:", error);
}
// Only protect admin routes
if (pathname.startsWith("/admin")) {
try {
const session = await auth.api.getSession({
headers: request.headers,
});
if (!session?.user) {
return NextResponse.redirect(new URL("/login", request.url));
}
// Check if user has admin/staff role
if (
!session.user.role ||
!["admin", "staff"].includes(session.user.role)
) {
return NextResponse.redirect(new URL("/", request.url));
}
} catch (error) {
console.error("Middleware auth error:", error);
return NextResponse.redirect(new URL("/login", request.url));
}
}
return NextResponse.next();
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
auth.ts
looks like this:
import { betterAuth } from "better-auth";
import { admin, apiKey } from "better-auth/plugins";
import { nextCookies } from "better-auth/next-js";
import { createAccessControl } from "better-auth/plugins/access";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "./db";
import { account, apikey, session, user, verification } from "./db/schema";
import { ac, userRole, adminRole, staffRole } from "./access-control";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
schema: {
user,
account,
session,
verification,
apikey,
},
}),
emailAndPassword: {
enabled: true,
requireEmailVerification: false,
},
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 1 day
},
plugins: [
admin({
ac,
roles: {
user: userRole,
staff: staffRole,
admin: adminRole,
},
}),
apiKey(),
nextCookies(), // Must be the last plugin in the array
],
});
export type Session = typeof auth.$Infer.Session;
import { betterAuth } from "better-auth";
import { admin, apiKey } from "better-auth/plugins";
import { nextCookies } from "better-auth/next-js";
import { createAccessControl } from "better-auth/plugins/access";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "./db";
import { account, apikey, session, user, verification } from "./db/schema";
import { ac, userRole, adminRole, staffRole } from "./access-control";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
schema: {
user,
account,
session,
verification,
apikey,
},
}),
emailAndPassword: {
enabled: true,
requireEmailVerification: false,
},
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 1 day
},
plugins: [
admin({
ac,
roles: {
user: userRole,
staff: staffRole,
admin: adminRole,
},
}),
apiKey(),
nextCookies(), // Must be the last plugin in the array
],
});
export type Session = typeof auth.$Infer.Session;
auth-client.ts
looks like this:
import { createAuthClient } from "better-auth/react";
import { inferAdditionalFields, adminClient } from "better-auth/client/plugins";
import type { auth } from "./auth";
import { ac, userRole, staffRole, adminRole } from "./access-control";
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
plugins: [
inferAdditionalFields<typeof auth>(),
adminClient({
ac,
roles: {
user: userRole,
staff: staffRole,
admin: adminRole,
},
}),
],
});
export const { useSession, signIn, signOut, signUp } = authClient;
import { createAuthClient } from "better-auth/react";
import { inferAdditionalFields, adminClient } from "better-auth/client/plugins";
import type { auth } from "./auth";
import { ac, userRole, staffRole, adminRole } from "./access-control";
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
plugins: [
inferAdditionalFields<typeof auth>(),
adminClient({
ac,
roles: {
user: userRole,
staff: staffRole,
admin: adminRole,
},
}),
],
});
export const { useSession, signIn, signOut, signUp } = authClient;
2 Replies