clerk/javascript

[next app router] Clerk cant detect authMiddleware when used in Layout

leetmachine opened this issue · 31 comments

Preliminary Checks

Reproduction / Replay Link

https://github.com/leetmachine/clerk-demo

Publishable key

pk_test_ZW1pbmVudC1zbmlwZS0xMC5jbGVyay5hY2NvdW50cy5kZXYk

Description

Steps to reproduce:

  1. clone repo https://github.com/leetmachine/clerk-demo
  2. npm install
  3. npm run dev
  4. navigate to homepage localhost:3000 and observe error appears in the console.

Expected behavior:
no error

Actual behavior:
observe error in console

 ⨯ node_modules/@clerk/nextjs/dist/esm/server/createGetAuth.js (26:12) @ eval
 ⨯ Error: Clerk: auth() was called but Clerk can't detect usage of authMiddleware(). Please ensure the following:
- authMiddleware() is used in your Next.js Middleware.
- Your Middleware matcher is configured to match this route or page.
- If you are using the src directory, make sure the Middleware file is inside of it.

For more details, see https://clerk.com/docs/quickstarts/nextjs

    at stringify (<anonymous>)
digest: "3101676233"
null

I Followed the general quickstart docs for @clerk/nextjs. I created a Header component and render it in the RootLayout. The Header component uses a handful of basic Clerk components. The error started appearing. Moving the Header to a page gets rid of the error.

Environment

System:
    OS: macOS 14.2.1
    CPU: (10) arm64 Apple M1 Max
    Memory: 1.89 GB / 32.00 GB
    Shell: 3.6.0 - /opt/homebrew/bin/fish
  Binaries:
    Node: 20.11.0 - ~/.local/share/nvm/v20.11.0/bin/node
    Yarn: 1.22.19 - /opt/homebrew/bin/yarn
    npm: 10.2.4 - ~/.local/share/nvm/v20.11.0/bin/npm
    pnpm: 8.9.0 - /opt/homebrew/bin/pnpm
  Browsers:
    Chrome: 121.0.6167.139
    Safari: 17.2.1
  npmPackages:
    @clerk/nextjs: ^4.29.6 => 4.29.6
    @types/node: ^20 => 20.11.16
    @types/react: ^18 => 18.2.55
    @types/react-dom: ^18 => 18.2.18
    autoprefixer: ^10.0.1 => 10.4.17
    eslint: ^8 => 8.56.0
    eslint-config-next: 14.1.0 => 14.1.0
    next: 14.1.0 => 14.1.0
    postcss: ^8 => 8.4.34
    react: ^18 => 18.2.0
    react-dom: ^18 => 18.2.0
    tailwindcss: ^3.3.0 => 3.4.1
    typescript: ^5 => 5.3.3

Same issue for me. Despite this error my app seems to work properly.

System:
OS: Windows 10 10.0.19045
CPU: (16) x64 11th Gen Intel(R) Core(TM) i9-11950H @ 2.60GHz
Memory: 12.01 GB / 31.23 GB
Binaries:
Node: 20.9.0 - C:\Program Files\nodejs\node.EXE
Yarn: 1.22.21 - ~\AppData\Roaming\npm\yarn.CMD
npm: 10.4.0 - C:\Program Files\nodejs\npm.CMD
pnpm: 8.15.1 - ~\AppData\Roaming\npm\pnpm.CMD
Browsers:
Edge: Chromium (121.0.2277.98)
Internet Explorer: 11.0.19041.3636
npmPackages:
@clerk/backend: ^0.38.0 => 0.38.0
@clerk/localizations: ^1.26.14 => 1.26.14
@clerk/nextjs: 5.0.0-beta-v5.20 => 5.0.0-beta-v5.20
@clerk/themes: ^1.7.9 => 1.7.9
@clerk/types: ^3.61.0 => 3.61.0
next: ^14.1.0 => 14.1.0

Hey @igorovic and @leetmachine! Thanks so much for opening up this issue and providing a reproduction - this helped us get to the bottom of it quickly, made a big difference. So, the solution to this is kind of interesting.

On the surface, it's quite simple: inside your Header component, there is an image that tries to pull /logo.svg, which doesn't exist in the public folder, so it 404s and shows a broken image. Removing this line or ensuring that the image path doesn't 404 will eliminate the auth middleware error right away. To get your app fixed and continue moving, you can do this and get back to work quickly.

Also, I'd recommend editing the middleware to add / as a publicPath - the way that you are utilizing <SignedIn> and <SignedOut> won't really work unless you do so - by default all pages are protected and redirect to a separate login page, so it's not possible for your app in the state it's in to actually render the <SignedOut> component, since if there's no user, you are redirected by the middleware before the page renders.

export default authMiddleware({ publicRoutes: ["/"] });

You may still be wondering though, why in the world did I get a middleware error with an invalid image path? Here's the full details:

  • Clerk's middleware does not match any sort of static assets by default, this is defined by the first regex in the matcher export from the middleware. If it did, any signed out user wouldn't be able to see images, css, etc which would be quite bad.
  • When next tried to load the logo image, it didn't find it, and fell back to rendering a 404 page. However, when next renders the 404 page, it also calls the root layout for some reason. Calling the root layout meant running Header, which uses the SignedOut component, which executes a hook to check auth state internally. This hook was unable to detect middleware, since it got skipped as the request was for an image, and then threw the error about not being able to find middleware. You can verify this by visiting localhost:3000/logo.svg directly in dev mode, where you get that error straight on the client.

This is an unusual issue for sure - luckily it's easily fixed, but still a very un-ideal behavior in general. We're investigating to see if there's a way that we can avoid this for anyone who runs into it in the future.

Thank you again for bringing this up and for using Clerk, and don't hesitate to reach out with any feedback or other issues you encounter while working on your app.

Thanks @jescalan! I had the same issue, but correct publicRoutes. And the problem is in the logo.svg.
I have an additional options in metadata for app icon and href attribute for icon must be writen without dot, just -> href="/logo.svg", NOT -> href="./logo.svg"

@Tatsiana1111 interesting! i thought that we had a patch into nextjs that should resolve this now. which version of nextjs were you using when you ran into this issue?

@jescalan next - 14.1.4, @clerk/netxjs - 4.29.12

I had the same problem, but none of the above helped.
In case anyone else has a problem and if this might help the guys at Clerk here is my code and solution:

  • Versions:
    "@clerk/nextjs": "^5.2.3",
    "@prisma/client": "^5.16.2",
    "next": "14.2.5",
    "react": "^18",
    "react-dom": "^18",
    "react-toastify": "^10.0.5"
  • Middleware written as suggested with clerkMiddleware
  • My Header component, as server component,
const Header = () => {
    return (
        <nav className="navbar">
            <div className="navbar-container">
                <h2>My app</h2>
                <div>
                    <SignedOut>
                        <SignInButton />
                    </SignedOut>
                    <SignedIn>
                        <UserButton />
                    </SignedIn>
                </div>
            </div>
        </nav>
    )
}

The problem was also present in the Header component, even though I didn’t have any SVGs or imported files.
In my case, the SignedIn and SignedOut components were causing the problem, because without them, the error disappeared.
In the end, with a little help from Clerk AI, the error was resolved by adding ‘use client’ at the top of the Header component with SignedIn and SignedOut components included.

First time trying to use Clerk. Not a great experience hitting this when creating a fresh next.js app then following https://clerk.com/docs/references/nextjs/clerk-middleware to add clerk and seeing a ton of these error messages in my logs.

@devth would you be able to share a copy of the repo you used to get started here? I just ran a test following the quickstart and didn't get any errors like this - want to see if I can pin down what happened in your case

The quickstart with ClerkProvider doesn't work right now, at least if you started with npx create-next-app@latest
https://clerk.com/docs/quickstarts/nextjs#add-clerk-provider-and-components-to-your-app

I found that ClerkProvider doesn't want to work in a "use server" environment and yet the Metadata of layout.tsx requires it.

So you need to do something like this

// layout.tsx
import { ClerkProvider } from "@clerk/nextjs";
import { Header } from "./header";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Neon-Next-Clerk-Inngest Example",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body className={inter.className}>
          <header>
            <Header />
          </header>
          <main>{children}</main>
        </body>
      </html>
    </ClerkProvider>
  );
}

Along with:

// header.tsx
"use client";
// the :point_up: use client was necessary to make this module work
import { SignInButton, SignedIn, SignedOut, UserButton } from "@clerk/nextjs";

export const Header = () => {
  return (
    <nav className="navbar">
      <div className="navbar-container">
        <div className="grid w-full flex-grow items-center bg-zinc-100 p-2 sm:justify-end">
          <SignedOut>
            <SignInButton />
          </SignedOut>
          <SignedIn>
            <UserButton />
          </SignedIn>
        </div>
      </div>
    </nav>
  );
};

The following (from the guide) doesn't work

// broken layout.tsx
import { ClerkProvider, SignInButton, SignedIn, SignedOut, UserButton } from '@clerk/nextjs'
import './globals.css';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body>
          <header>
            <SignedOut>
              <SignInButton />
            </SignedOut>
            <SignedIn>
              <UserButton />
            </SignedIn>
          </header>
          <main>
            {children}
          </main>
        </body>
      </html>
    </ClerkProvider>
  )
}

@clarkbw oof - thanks for calling this out. just managed to reproduce. we are going to get this patched up today, so sorry!

Hey @clarkbw - we investigated this quite thoroughly and have tracked down the source of the problem.

The good news: You can ignore the error message, and it will not impact the functionality of your app, despite being annoying.
The bad news: This is a fairly thorny issue with the way that nextjs works which some pieces are out of our ability to directly fix, so we won't be able to get a fix in for it today and will need to coordinate with the nextjs team a little. I drop another update when I have one!

@jescalan thanks for the quick turn around on the investigation and status updates! To be clear the error I got wasn't ignorable, though it could be we are all seeing slightly different errors. When I went through the guide I got the following errors in the console and then an Unhandled Runtime Error (in screenshot) in the web which didn't allow you to do anything else.

The change I made above allowed me to use Clerk and Next and it's not too off when compared to the original implementation.

console error

 ⨯ Error: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware() (or the deprecated authMiddleware()). Please ensure the following:
-  clerkMiddleware() (or the deprecated authMiddleware()) is used in your Next.js Middleware.
- Your Middleware matcher is configured to match this route or page.
- If you are using the src directory, make sure the Middleware file is inside of it.

For more details, see https://clerk.com/docs/quickstarts/nextjs

    at stringify (<anonymous>)
    at stringify (<anonymous>)
digest: "2861798844"
 ⨯ Error: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware() (or the deprecated authMiddleware()). Please ensure the following:
-  clerkMiddleware() (or the deprecated authMiddleware()) is used in your Next.js Middleware.
- Your Middleware matcher is configured to match this route or page.
- If you are using the src directory, make sure the Middleware file is inside of it.

Unhandled Runtime Error

Screenshot 2024-07-19 at 11 23 00 AM

verions

for clarity here are the version i am using

    "@clerk/nextjs": "^5.2.4",
    "@neondatabase/serverless": "^0.9.4",
    "drizzle-orm": "^0.32.0",
    "next": "14.2.5",
    "react": "^18",
    "react-dom": "^18"

@jescalan I can try to get my code back to a sharable state if you still need it for debugging.

@clarkbw and anyone else following, could you try npm i @clerk/nextjs@5.2.5-snapshot.v910e99d --save-exact to see if that resolves it for you? 🙏

That fixed the errors in my server logs 👍🏻

😿 That didn't solve the issue for me, I published my code here if that's any help: https://github.com/clarkbw/neon-nextjs-drizzle-clerk-inngest

😿 That didn't solve the issue for me, I published my code here if that's any help: https://github.com/clarkbw/neon-nextjs-drizzle-clerk-inngest

Hello @clarkbw 👋🏻 Just tested your repo locally and it seems that there's something weird going on with the name of your middleware file. I tried renaming it to middleware.ts just in case because I see the it never ran (unrelated to Clerk). After renaming it, git status reports:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   src/middleware.ts
        deleted:    src/middleware.ts 

and all Clerk functionality works as expected (after upgrading to the snapshot version we shared above) even without the if(false) and the use client workarounds. Can you give it a try and let me know if it works?

PS: The matcher you're using is very similar but not identical to the one we currently recommend in our docs. This is not related to your issue here, but In any case, we're going to document a different default matcher next week - you might as well use it from now :) The new matcher is:

export const config = {
  matcher: [
    // Skip all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for api routes
    '/(api|trpc)(.*)',
  ],
};

@nikosdouvlis oh wow, thanks for checking that out and fixing it! I copied the file name from the guide but I'm guessing there are some weird characters somewhere in it. That should fix me up. Again, thanks for your time.

Everything is working now, thanks again ❤️

PS: The matcher you're using is very similar but not identical to the one we currently recommend in our docs.

I believe I grabbed that matcher from here

@clarkbw and anyone else following, could you try npm i @clerk/nextjs@5.2.5-snapshot.v910e99d --save-exact to see if that resolves it for you? 🙏

Can confirm, I ran bun a @clerk/nextjs@5.2.5-snapshot.v910e99d, restarted the dev server, and I don't get the error anymore in the server logs 🙌

Perfect - thanks everyone! We will aim to get this out on Monday in a stable release 🙏

Hello everyone 👋🏻 The fix has been released with @clerk/nextjs@5.2.5 so you no longer need to use the snapshot version. Thanks :)

Unless there is something I misunderstood about how auth is expected to work on api routes, I believe that there is still an issue : it all works well when api calls come from a client component but always fails when they come from the server. it's really hard to track but I get this error as soon as I fetch from a server component.
TypeError: Cannot read properties of null (reading 'useContext')

@pappitti Can you open a separate issue with a reproduction? Thanks!

I can admit that i am experiencing same thing, here is my clerk middleware

import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

const isPublicRoute = createRouteMatcher(["/"]);

export default clerkMiddleware((auth, request) => {
  if (!isPublicRoute(request)) {
    auth().protect();
  }
});

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
    // Always run for API routes
    "/(api|trpc)(.*)",
  ],
};

and the error i get:

⨯ Error: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware() (or the deprecated authMiddleware()). Please ensure the following:
-  clerkMiddleware() (or the deprecated authMiddleware()) is used in your Next.js Middleware.
- Your Middleware matcher is configured to match this route or page.
- If you are using the src directory, make sure the Middleware file is inside of it.

For more details, see https://clerk.com/docs/quickstarts/nextjs

Auth flow is working its protecting whole app but still getting this error.
My middleware file is in my project root near of next.config.js file

Hey there @devhik0 👋🏻 Just wanted to check - are you using the latest version of @clerk/nextjs?
If yes, could you check your server console and see if there are any 404 logs for static files? This is the only case I currently know of that could throw this error while the middleware is set up correctly.

Alternatively, if you could provide a minimal repro I'd be happy to take a look.

Edit: I think it's worth explaining why a missing static file would throw the error in case it's helpful for anyone reaching this thread. In app router, layouts still render for 404 pages. If a request is made for a missing static file, nextjs will try to render the layout for the 404 page. The middleware, however, will not run as our default matcher does not match static files, so auth() will log the error Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware() error

Yes i am using latest version. There is no 404 at my logs

 GET /calendar 200 in 112ms
 GET /calendar 200 in 61ms

Oh okay then, even if i see it i will ignore next time 😄 . I thought that it was causing from some configuration mismatch or auth settings. Thanks. @nikosdouvlis

The only thing that worked for me was to create a different component and make it a client component and then import it to the layout or anywhere in your route files.

// header.tsx
"use client";
// the :point_up: use client was necessary to make this module work
import { SignInButton, SignedIn, SignedOut, UserButton } from "@clerk/nextjs";

export const Header = () => {
  return (
    <nav className="navbar">
      <div className="navbar-container">
        <div className="grid w-full flex-grow items-center bg-zinc-100 p-2 sm:justify-end">
          <SignedOut>
            <SignInButton />
          </SignedOut>
          <SignedIn>
            <UserButton />
          </SignedIn>
        </div>
      </div>
    </nav>
  );
};

// layout.tsx
import type { Metadata } from "next";
import { Inter as FontSans } from "next/font/google";
import "./globals.css";
import { cn } from "@/lib/utils";
import { ClerkProvider } from "@clerk/nextjs";
import { Header } from "@/components/Header";

const fontSans = FontSans({
  subsets: ["latin"],
  variable: "--font-sans",
});

const inter = FontSans({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Pledge",
  description: "An online platform for having online contract signings.",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body
          className={cn(
            "min-h-screen bg-background font-sans antialiased",
            fontSans.variable
          )}
        >
          <header>
            <Header />
          </header>
          <main>{children}</main>
        </body>
      </html>
    </ClerkProvider>
  );
}

@pappitti Can you open a separate issue with a reproduction? Thanks!

nevermind I got to the conclusion that it was a case of "not understanding how it is expected to work".
Long story short : auth() works on both client and server components. For APi routes, it only works if the api routes is called from a client component. it seems to be how it is expected to be. If anyone struggles with this, here is the solution: move your api logic to other folders/files, i.e what Nextjs call server actions. You can call server actions from both types of components (and from your api if you still need it).

Still facing the same issue , below are versions of the package I used.

image

"dependencies": { "@clerk/nextjs": "^5.3.2", "next": "^14.2.4", "react": "^18.3.1", "react-dom": "^18.3.1", },

middleware.ts
`
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

const isProtectedRoute = createRouteMatcher(["/dashboard(.*)"]);

export default clerkMiddleware((auth, request) => {
if (isProtectedRoute(request)) auth().protect();
});

export const config = {
matcher: [
// Skip all static files, unless found in search params
"/((?!_next|[^?]\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).)",
// Always run for api routes
"/(api|trpc)(.*)",
],
};

`

@yc-codehack I think it might be better to open up a support request with us for issues like this!