Open tobiasmeyhoefer opened 6 months ago
having the same issue using prisma neon adaptor
having the same issue with planetscale adapter
I am having the same issue, I am using App Router (v14), Prisma, Neon and Next-Auth v5.
I'm having the same issue while implementing Role Based authentication using Next-auth v5. Everything works fine in development but edge function gets timed out in production.
Note: It's not a Vercel issue as I've tried Netlify too and got the same result. Having this issue for two weeks now.
Next-auth v4 doesn't have this kinda issues. But as I want to implement auth using v5, I've tried multiple ways of fixing this issue and nothing seems to be working :)
Depending on your setup, you may be running into issues where your adapter is not "edge compatible" and is timing out trying to start it up, etc. Please check out our edge compatibility guide.
If you've confirmed your adapter is "edge compatible", then you may also be doing too many DB actions in your middleware. The limit there seems to be "25s to begin returning data".
That's an awfully long time though, so unless yuo're doing something extra heavy you shouldn't be hitting that limit without other issues I'd guess.
Please provide some more details, like versions of all the relevant packages and some log output and we can try and figure out what's going on :pray:
This is the only log I have in production:
[GET] [middleware: "middleware"] /dashboard/company reason=EDGE_FUNCTION_INVOCATION_TIMEOUT, status=504, user_error=true
Versions:
"@auth/prisma-adapter": "^2.0.0",
"next-auth": "5.0.0-beta.17",'
Here is my db instance:
import { Client } from "@planetscale/database"
import { PrismaPlanetScale } from "@prisma/adapter-planetscale"
import { PrismaClient } from "@prisma/client"
import { env } from "@/env.mjs"
const connectionString = `${env.DATABASE_URL}`
const client = new Client({ url: connectionString })
const adapter = new PrismaPlanetScale(client)
const prisma = new PrismaClient({ adapter })
export const db = prisma
My auth.ts:
import { PrismaAdapter } from "@auth/prisma-adapter"
import NextAuth from "next-auth"
import type { Provider } from "next-auth/providers"
import GitHub from "next-auth/providers/github"
import Google from "next-auth/providers/google"
import { db } from "@/lib/db"
const providers: Provider[] = [GitHub, Google]
export const config = {
adapter: PrismaAdapter(db),
providers: providers,
pages: {
signIn: "/login",
},
callbacks: {
async session({ token, session }) {
if (token) {
session.user.id = token.id
session.user.name = token.name
session.user.email = token.email
session.user.image = token.picture
}
return session
},
async jwt({ token, user }) {
const dbUser = await db.user.findFirst({
where: {
email: token.email,
},
})
if (!dbUser) {
if (user) {
token.id = user?.id
}
return token
}
return {
id: dbUser.id,
name: dbUser.name,
email: dbUser.email,
picture: dbUser.image,
}
},
},
}
export const { handlers, auth, signIn, signOut } = NextAuth(config)
And my middleware:
import { NextResponse } from "next/server"
import { auth } from "@/auth"
import {
DEFUALT_LOGIN_REDIRECT,
apiAuthPrefix,
authRoutes,
publicRoutes,
} from "@/routes/auth"
export default auth((req) => {
const { nextUrl } = req
const isLoggedIn = !!req.auth
const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix)
const isPublicRoute = publicRoutes.includes(nextUrl.pathname)
const isAuthRoute = authRoutes.includes(nextUrl.pathname)
if (isApiAuthRoute) {
return
}
if (isAuthRoute) {
if (isLoggedIn) {
return NextResponse.redirect(new URL(DEFUALT_LOGIN_REDIRECT, nextUrl))
}
return
}
if (!isLoggedIn && !isPublicRoute) {
return NextResponse.redirect(new URL("/login", nextUrl))
}
return
})
export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
}
@Yvon-Data can you share the versions of all the relevant prisma packages as well?
Of course!
"prisma": "^5.13.0",
"@prisma/adapter-planetscale": "^5.13.0",
"@prisma/client": "^5.13.0"
Of course!
"prisma": "^5.13.0", "@prisma/adapter-planetscale": "^5.13.0", "@prisma/client": "^5.13.0"
Hmm okay so it looks like that should be "edge compatible", but your middleware / auth.js setup is relatively simple so I can't imagine its anything else other than the DB lookup in your jwt callback 🤔
Might be worthwhile testing another DB call in an unrelated edge api route, like shown in this prisma + planetscale doc: https://www.prisma.io/docs/orm/prisma-client/deployment/edge/deploy-to-vercel#planetscale, to see if you run into similar errors there or if that works as expected
I downgraded next-auth to v4 and now its working like a charm. I was also using edge compatible prisma but the issue is with next-auth v5.
I'm having the same timeout issues. Works locally but not when deployed to Vercel. Here's my information:
Vercel logs: [GET] [middleware: "src/middleware"] /games reason=EDGE_FUNCTION_INVOCATION_TIMEOUT, status=504, user_error=true
auth.ts:
import NextAuth from 'next-auth';
import { PrismaAdapter } from '@auth/prisma-adapter';
import prisma from '@/lib/prisma';
import Google from 'next-auth/providers/google';
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [Google],
});
schema.prisma:
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
datasource db {
provider = "postgresql"
url = env("POSTGRES_PRISMA_URL") // uses connection pooling
directUrl = env("POSTGRES_URL_NON_POOLING") // uses a direct connection
}
prisma.ts:
import { PrismaClient } from '@prisma/client';
import { PrismaNeon } from '@prisma/adapter-neon';
import { Pool } from '@neondatabase/serverless';
let prisma: PrismaClient;
declare global {
var prisma: PrismaClient;
}
const createPrismaClient = () => {
const neon = new Pool({ connectionString: process.env.POSTGRES_PRISMA_URL });
const adapter = new PrismaNeon(neon);
const prisma = new PrismaClient({ adapter });
return prisma;
};
if (process.env.NODE_ENV === 'production') {
prisma = createPrismaClient();
} else {
if (!global.prisma) {
global.prisma = createPrismaClient();
}
prisma = global.prisma;
}
export default prisma;
middleware.ts:
export { auth as middleware } from '@/lib/auth';
package.json:
"@auth/prisma-adapter": "^2.2.0",
"@neondatabase/serverless": "^0.9.3",
"@prisma/adapter-neon": "^5.15.0",
"next": "14.2.4",
"next-auth": "^5.0.0-beta.19",
"prisma": "^5.15.0",
Not sure I need to be instantiating the prisma client this way but kinda just following the docs. Please let me know if there's any other information I can use to resolve this issue. It seems based on the documentation that this setup should be edge compatible.
What helps me was securing that drizzle is used in the correct way . Look at this guide: https://orm.drizzle.team/learn/tutorials/drizzle-with-vercel-edge-functions
To fix the middleware timing out I explicitly set the runtime to edge with export const runtime = 'experimental-edge';
Tried doing the same with the api/auth/[...nextauth]/route.ts
route by adding export const runtime = 'edge';
, but then I get a strange error Error: The Edge Function "api/auth/[...nextauth]" size is 1 MB and your plan size limit is 1 MB. Learn More: https://vercel.link/edge-function-size
even though despite this from build:
├ ƒ /api/auth/[...nextauth] 0 B 0 B
I am having the same issues in production. Everything seems fine during development but once deployed to Vercel approximately 1/5 requests going through middleware will timeout with a similar error:
[GET] [middleware: "src/middleware"] /en/app/settings reason=INTERNAL_EDGE_FUNCTION_INVOCATION_FAILED, status=500, upstream_status=500, user_error=false
package.json
"@auth/prisma-adapter": "^2.4.1",
"@prisma/client": "^5.16.1",
"@prisma/extension-accelerate": "^1.1.0",
"prisma": "^5.16.1",
"next": "^14.2.4",
"next-auth": "5.0.0-beta.19"
db.ts
import { PrismaClient } from "@prisma/client/edge";
import { withAccelerate } from '@prisma/extension-accelerate'
import { env } from "~/env";
const createPrismaClient = () =>
new PrismaClient({
log:
env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
}).$extends(withAccelerate());
const globalForPrisma = globalThis as unknown as {
prisma: ReturnType<typeof createPrismaClient> | undefined;
};
export const db = globalForPrisma.prisma ?? createPrismaClient();
if (env.NODE_ENV !== "production") globalForPrisma.prisma = db;
middleware.ts
import { auth } from "./server/auth";
import { NextResponse } from "next/server";
export default auth((req) => {
//redirect to sign in page if not authenticated
if (!req.auth && req.nextUrl.pathname !== "/auth/signin") {
const newUrl = new URL("/auth/signin", req.nextUrl.origin)
return Response.redirect(newUrl)
}
});
export const config = { matcher: ["/app/:path*"] };
I tried adding export const runtime = 'experimental-edge'; as mentioned above but this did not make any difference for me.
One thing that helped me was getting rid of the neon websocket adapter. I had:
import ws from "ws";
if (typeof WebSocket === "undefined") {
neonConfig.webSocketConstructor = ws;
}
const connectionString = process.env.DATABASE_URL;
const pool = new Pool({ connectionString });
const adapter = new PrismaNeon(pool);
export const prisma = new PrismaClient({ adapter });
Instead, I switched it to a normal pool, and I stopped getting the timeouts.
const neon = new Pool({
connectionString: process.env.DATABASE_URL,
});
const adapter = new PrismaNeon(neon);
export const prisma = new PrismaClient({ adapter });
Had a lot of problems with this exact setup. I finally got it working, here's what I have.
import { drizzle } from "drizzle-orm/neon-http";
import { neon } from "@neondatabase/serverless";
import NextAuth from "next-auth"
import { DrizzleAdapter } from "@auth/drizzle-adapter"
const {
DATABASE_URL
} = process.env
const databaseConnection = () => {
const sql = neon(DATABASE_URL!);
const db = drizzle(sql);
return db;
}
const drizzleAdapter = () => {
const db = databaseConnection();
return DrizzleAdapter(db)
}
export const { auth, handlers, signIn, signOut } = NextAuth({
adapter: drizzleAdapter(),
providers: [ ... ]
})
import { handlers } from "@/auth"
import { NextRequest } from "next/server"
// more info here: https://github.com/nextauthjs/next-auth/issues/10928#issuecomment-2162893683
const reqWithTrustedOrigin = (req: NextRequest): NextRequest => {
if (process.env.AUTH_TRUST_HOST !== 'true') return req
const proto = req.headers.get('x-forwarded-proto')
const host = req.headers.get('x-forwarded-host')
if (!proto || !host) {
console.warn("Missing x-forwarded-proto or x-forwarded-host headers.")
return req
}
const envOrigin = `${proto}://${host}`
const { href, origin } = req.nextUrl
return new NextRequest(href.replace(origin, envOrigin), req)
}
export const GET = (req: NextRequest) => {
return handlers.GET(reqWithTrustedOrigin(req))
}
export const POST = (req: NextRequest) => {
return handlers.POST(reqWithTrustedOrigin(req))
}
Having the same issue.
Interestingly, disabling prefetching in all my Link components throughout the app mitigates the issue, but it still happens relatively often.
Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!
I'm having the same problem with basically the default setup. My db calls are pretty small (compared to other apps I have) so I'm just not sure of the problem. I've tried the above options. Running on the edge etc. Should I downgrade to v4?
"@auth/prisma-adapter": "^2.4.2",
"@neondatabase/serverless": "^0.9.4",
"@prisma/adapter-neon": "^5.17.0",
"@prisma/client": "^5.18.0",
"prisma": "^5.18.0",
"next": "14.2.5",
"next-auth": "^5.0.0-beta.20",
`//middleware.ts
export { auth as middleware } from "@/auth"`
//auth.ts
import NextAuth from "next-auth"
import { PrismaAdapter } from "@auth/prisma-adapter"
import prisma from "@/lib/prisma"
import Google from "next-auth/providers/google"
export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [Google],
})
// app\api\auth\[...nextauth]\route.ts
import { handlers } from "@/auth"
export const { GET, POST } = handlers
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
datasource db {
provider = "postgresql"
url = env("POSTGRES_PRISMA_URL")
directUrl = env("POSTGRES_URL_NON_POOLING")
}
model User {
// /lib/prisma.ts
import { PrismaClient } from "@prisma/client";
import { PrismaNeon } from '@prisma/adapter-neon'
import { Pool } from '@neondatabase/serverless'
declare global {
var prisma: PrismaClient | undefined;
}
const createPrismaClient = () => {
const pool = new Pool({ connectionString: process.env.POSTGRES_PRISMA_URL })
const adapter = new PrismaNeon(pool)
return new PrismaClient({ adapter })
}
const prisma = global.prisma || createPrismaClient()
if (process.env.NODE_ENV === "development") global.prisma = prisma;
export default prisma;
So i plugged the code and docs into Claude and after many iterations the below seems to work. Caveat: I don't know why, I can barely see any difference in what it does but for now it seems to work (MAJOR PROBLEM THOUGH: images do not seem to work, which isn't the end of the world for me but may be a deal breaker) if anyone can comment or help then that would be great
// middleware.ts
import { NextResponse } from 'next/server'
import { auth } from "./auth"
export default auth((req) => {
// Exclude image and static asset requests from middleware processing
if (
req.nextUrl.pathname.startsWith('/_next') ||
req.nextUrl.pathname.startsWith('/test') ||
req.nextUrl.pathname.startsWith('/static') ||
req.nextUrl.pathname.startsWith('/public') ||
req.nextUrl.pathname.endsWith('.webp') ||
req.nextUrl.pathname.endsWith('.png') ||
req.nextUrl.pathname.endsWith('.jpg') ||
req.nextUrl.pathname.endsWith('.jpeg') ||
req.nextUrl.pathname.endsWith('.gif')
) {
return NextResponse.next()
}
const isLoggedIn = !!req.auth
const isAuthPage = req.nextUrl.pathname.startsWith('/api/auth')
const isPublicPage = ['/', '/login'].includes(req.nextUrl.pathname) // Add other public pages here
if (!isLoggedIn && !isAuthPage && !isPublicPage) {
return NextResponse.redirect(new URL('/api/auth/signin', req.url))
}
return NextResponse.next()
})
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|_next/image|public|favicon.ico).*)',
],
}
// auth.ts
import NextAuth from "next-auth"
import { authConfig } from "./auth.config"
import { PrismaAdapter } from "@auth/prisma-adapter"
import prisma from "@/lib/prisma"
export const {
handlers: { GET, POST },
auth,
signIn,
signOut,
} = NextAuth({
...authConfig,
adapter: PrismaAdapter(prisma),
session: { strategy: "jwt" },
})
// auth.config.ts
import type { NextAuthConfig } from "next-auth"
import Google from "next-auth/providers/google"
export const authConfig = {
providers: [Google],
callbacks: {
authorized({ auth, request: { nextUrl } }) {
const isLoggedIn = !!auth?.user
const isOnDashboard = nextUrl.pathname.startsWith('/dashboard')
if (isOnDashboard) {
if (isLoggedIn) return true
return false // Redirect unauthenticated users to login page
} else if (isLoggedIn) {
return Response.redirect(new URL('/dashboard', nextUrl))
}
return true
},
},
} satisfies NextAuthConfig
// app\api\auth\[...nextauth]\route.ts
import { GET, POST } from "@/auth"
export { GET, POST }
So i plugged the code and docs into Claude and after many iterations the below seems to work. Caveat: I don't know why, I can barely see any difference in what it does but for now it seems to work (MAJOR PROBLEM THOUGH: images do not seem to work, which isn't the end of the world for me but may be a deal breaker) if anyone can comment or help then that would be great
It looks your middleware is already not running on some of the routes that you've excluded from processing in your middleware.ts
(/next
, /static
, /public
). You could update your config to also not match on those image extensions.
d'oh, didn't have the remote pattern in next.config
const nextConfig = { images: { remotePatterns: [ {
All works well now
For me it was also the websocket as @mattgraphlan pointed out.
We added a db call in the session callback, so everytime the auth method in the middleware was called there was an additional db call. That caused our timeout.
The DB client we used looked like this:
import { Pool } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-serverless";
import { env } from "./env";
import * as schema from "./schema";
const pool = new Pool({ connectionString: env.DATABASE_URL });
const db = drizzle(pool, { schema });
export default db;
export { db };
We switched to the http client:
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
import { env } from "./env";
import * as schema from "./schema";
const sql = neon(env.DATABASE_URL);
const db = drizzle(sql, { schema });
export default db;
export { db };
then everything worked fine.
Since you cannot use transactions with the http client. And the usual pg driver has a better performance in node runtimes, we used another driver, if it runs in a node environment:
import { drizzle } from "drizzle-orm/node-postgres";
import pg from "pg";
import { env } from "./env";
import * as schema from "./schema";
const { Pool } = pg;
const pool = new Pool({ connectionString: env.DATABASE_URL });
const db = drizzle(pool, { schema });
export default db;
export { db };
The switch is simply done by the package.json
of the db client through conditional exports:
{
...
"exports": {
"./client": {
"types": "./dist/client.node.d.ts",
"edge-light": "./src/client.edge.ts",
"node": "./src/client.node.ts"
},
"./schema": {
"types": "./dist/schema/index.d.ts",
"default": "./src/schema/index.ts"
}
},
...
}
I got it fixed and talk about this and a couple other problems you might face in this Video:
I got it fixed and talk about this and a couple other problems you might face in this Video:
I'm dying for a solution to this. Your method works, but it doesn't verify that the session is actually valid. Someone can easily spoof the cookie and trick your app into thinking they are signed in...
After spending several weeks on this and trying a million different ways, I figured out a solution 😎. This uses JWT while still ensuring the session has the latest user data I need the user data to be current because the roles
of a user can change and it needs to be reflected instantly.
Also, I am using neon
for my database which provides a Prisma adapter that allows it to be used at the edge. In addition, this adapter supports WebSockets
which further helps our performance.
In my authConfig.ts
, I have (session set to jwt strategy)
adapter: PrismaAdapter(prisma),
session: {
strategy: "jwt"
}
And for the session
callback in authConfig.ts
, I have the following
session: async ({ session, token }) => {
const user = await prisma.user.findUniqueOrThrow({ where: { id: token.sub } });
return { ...session, user };
}
Now for the most important part- I have an edgeAuth.ts
file that I call from the middleware/edge. I import my base config from authConfig.ts
and simply override the session callback to not fetch the user from the database.
const config = authConfig(prisma);
export const { auth } = NextAuth({
...config,
callbacks: {
...config.callbacks,
session: async ({ session }) => session
}
});
prisma.ts
declare global {
var prisma: PrismaClient | undefined;
}
neonConfig.webSocketConstructor = WebSocket;
const createPrismaClient = () => {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const adapter = new PrismaNeon(pool);
return new PrismaClient({ adapter })
};
const prisma = global.prisma || createPrismaClient();
if (process.env.NODE_ENV === "development") global.prisma = prisma;
export default prisma;
Finally, my middleware.ts
as an example
import { auth } from "./utils/auth/edgeAuth";
export const config = {
matcher: ["/admin/:path*"]
};
export const middleware = auth((req) => {
// Redirect to app if user is not an admin and tries to access private route.
if (req.nextUrl.pathname.startsWith("/admin") && !req.auth) {
return Response.redirect(new URL("/", req.nextUrl.origin));
}
});
Having the same issue with the Prisma Neon Adapter. Curious if there is a fix to this.
Same issues with Drizzle Neon Serverless adapter
import { Pool } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-serverless";
const pool = new Pool({
connectionString: serverConfig.databaseConnectionString,
});
export const db = drizzle(pool, {});
The problem is that authjs + db adapter + edge dont work well together.
The documentation goes into detail about this here: https://authjs.dev/guides/edge-compatibility
Look at Split Config
for a solution.
Here's how I did it
auth.config.js
(NOTE: no db adapter hooked up)
import type { NextAuthConfig } from "next-auth";
import { type Provider } from "next-auth/providers";
import GoogleProvider from "next-auth/providers/google";
import { env } from "../env.mjs";
export const providers: Provider[] = [
GoogleProvider({
clientId: env.AUTH_GOOGLE_CLIENT_ID,
clientSecret: env.AUTH_GOOGLE_CLIENT_SECRET,
// since we trust google has securely verified the email for all users
// we can automatically link the user to an account in the db
// https://next-auth.js.org/configuration/providers/oauth#allowdangerousemailaccountlinking-option
allowDangerousEmailAccountLinking: true,
profile(_profile) {
return {
id: _profile.sub,
firstName: _profile.given_name,
lastName: _profile.family_name,
email: _profile.email,
imageUrl: _profile.picture,
role: null,
};
},
authorization: {
params: {
prompt: "consent",
access_type: "offline",
response_type: "code",
},
},
}),
];
// this is a next auth config object that only has the providers
// and DOES NOT have a db adapater. This is so that we can use this
// at the edge (such as in middleware)
// read more here: https://authjs.dev/guides/edge-compatibility
export default {
theme: {
logo: "/logo.png",
},
providers,
session: {
// jwt is required for us, because our middleware runs at the edge
// and we cant make prisma calls there
strategy: "jwt",
},
callbacks: {
jwt({ token, user }) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (user?.id) {
// User is available during sign-in
token.firstName = user.firstName;
token.lastName = user.lastName;
token.role = user.role;
token.id = user.id;
}
return token;
},
session({ token, session }) {
return {
...session,
user: {
id: token.id,
role: token.role,
firstName: token.firstName,
lastName: token.lastName,
imageUrl: token.imageUrl,
},
};
},
},
} satisfies NextAuthConfig;
then my middleware.ts
import authConfig from "~/auth/auth.config";
const { auth } = NextAuth(authConfig);
export default auth((req) => {
...
}
and finally my auth object with db adapter in auth.ts
import NextAuth from "next-auth";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { prisma } from "@/db";
import authConfig from "./auth.config";
export const { auth, handlers, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma) as Adapter,
...authConfig,
} satisfies NextAuthConfig);
and nextauth.d.ts
import { type User as DatabaseUser } from "@/db/prisma/zod/modelSchema/UserSchema";
import { type AgentProfile } from "@/db/prisma/zod/modelSchema/AgentProfileSchema";
import "next-auth/jwt";
type SessionUser = DatabaseUser> & {
brokerageId: string | null;
};
declare module "next-auth/adapters" {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface AdapterUser extends SessionUser {}
}
declare module "next-auth/jwt" {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface JWT extends SessionUser {}
}
declare module "next-auth" {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface User extends SessionUser {}
interface Session {
user: SessionUser;
}
}
Environment
Reproduction URL
https://github.com/tobiasmeyhoefer/bitz
Describe the issue
In localhost everything works fine but when deployed with Vercel hitting this error every second or third request when the middleware gets called...
long waiting and then this example error message: [GET] [middleware: "middleware"] /browse reason=EDGE_FUNCTION_INVOCATION_TIMEOUT, status=504, user_error=true
I already tried different package versions and setting location near me
How to reproduce
you can clone and try to deploy it yourself or look here: https://bitz-ecru.vercel.app
Im using neon serverless database... one thing that is sus to me is that the file db.ts:
gets called every time, shouldn't the connection be established only once?
Expected behavior
no errors at all