clerk/javascript

V5: `OrganizationProfile` throws error upon logout

oferitz opened this issue · 15 comments

Preliminary Checks

Reproduction

Publishable key

Description

The issue only happens in V5.

Using the OrganizationProfile in Next.js server component:
/org/[[...org]]/page

export default function OrganizationPage() {
   return (
   <>
      
        Loading...
      
      
        
      
    
   )
}

So far all good
org_profile

  1. But when signing out with the UserButton
      <SignedIn>
        <UserButton />
      </SignedIn>

sign_out

  1. You get this screen for a second and redirected to /sign-in page as expected

error

Like I mentioned above, the exact same flow works in V4 without errors, so I guess it's some kind of version regression. I want to upgrade to V5 but can't currently do so due to this issue. Please check, thanks.

Environment

System:
    OS: macOS 14.4.1
    CPU: (11) arm64 Apple M3 Pro
    Memory: 733.31 MB / 36.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.11.1 - /usr/local/bin/node
    npm: 10.2.4 - /usr/local/bin/npm
    pnpm: 9.0.4 - /opt/homebrew/bin/pnpm
  Browsers:
    Chrome: 123.0.6312.124
    Safari: 17.4.1
  npmPackages:
    @biomejs/biome: 1.7.0 => 1.7.0
    @clerk/nextjs: 5.0.1 => 5.0.1
    @clerk/themes: 1.7.13 => 1.7.13
    @clerk/types: 4.0.0 => 4.0.0
    @datadog/browser-rum: 5.14.0 => 5.14.0
    @hookform/resolvers: 3.3.4 => 3.3.4
    @nextui-org/react: 2.3.4 => 2.3.4
    @playwright/test: 1.42.1 => 1.42.1
    @splitsoftware/splitio: 10.25.2 => 10.25.2
    @splitsoftware/splitio-react: 1.11.1 => 1.11.1
    @tanstack/react-query: 5.28.14 => 5.28.14
    @tanstack/react-table: 8.16.0 => 8.16.0
    @testing-library/react: 15.0.2 => 15.0.2
    @types/node: 20 => 20.12.7
    @types/react: 18 => 18.2.79
    @types/react-dom: 18 => 18.2.25
    @vitejs/plugin-react: 4.2.1 => 4.2.1
    autoprefixer: 10.4.18 => 10.4.18
    axios: 1.6.8 => 1.6.8
    clsx: 2.1.0 => 2.1.0
    dayjs: 1.11.10 => 1.11.10
    fast-deep-equal: 3.1.3 => 3.1.3
    framer-motion: 11.0.24 => 11.0.24
    jotai: 2.8.0 => 2.8.0
    jsdom: 24.0.0 => 24.0.0
    lucide-react: 0.364.0 => 0.364.0
    next: 14.2.2 => 14.2.2
    next-themes: 0.2.1 => 0.2.1
    nextjs-toploader: 1.6.11 => 1.6.11
    orval: 6.26.0 => 6.26.0
    postcss: 8 => 8.4.38
    prettier: 3.2.5 => 3.2.5
    react: 18 => 18.2.0
    react-confetti: 6.1.0 => 6.1.0
    react-dom: 18 => 18.2.0
    react-hook-form: 7.51.3 => 7.51.3
    reactflow: 11.11.0 => 11.11.0
    recharts: 2.12.5 => 2.12.5
    sonner: 1.4.41 => 1.4.41
    tailwind-merge: 2.2.2 => 2.2.2
    tailwindcss: 3.4.3 => 3.4.3
    typescript: 5 => 5.4.5
    vite-tsconfig-paths: 4.3.2 => 4.3.2
    vitest: 1.5.0 => 1.5.0
    zod: 3.22.4 => 3.22.4

Hi!

Sorry to hear you're running into an issue. To help us best begin debugging the underlying cause, it is incredibly helpful if you're able to create a minimal reproduction. This is a simplified example of the issue that makes it clear and obvious what the issue is and how we can begin to debug it.

If you're up for it, we'd very much appreciate if you could provide a minimal reproduction and we'll be able to take another look.

Thanks for using Clerk!

Hey @oferitz thanks for reporting this. For quickly confirming if the issue has been fixed, you could add do this

<ClerkProvider clerkJSVersion="5.2.0-snapshot.vfc5e305" {...} />

@panteliselef Thanks! It does solve the original issue, but it strangely introduces a new one:
Screenshot 2024-04-30 at 14 03 16

Even though my page is defined with catch-all routes
Screenshot 2024-04-30 at 14 03 31

Yeah, this seems to be unrelated, we are already tracking this internally.

Do you mind doing this explicitly ?

<OrganizationProfile
   routing="path"
   path="/organization"
/>

@panteliselef Sure, do i need to change the folder structure? because doing this with the current catch-all doesn't seem to render anything on the screen.

Is /organization a nested route ? I couldn't tell from the screenshot above, but yes the file structure should match the path.

OK i made the necessary changes and it renders now, but the error is still present upon signout:

Error: Clerk: The "/main/organization" route is not a catch-all route. It is recommended to convert this route to a catch-all route, eg: "/main/organization/[[...rest]]/page.tsx". Alternatively, update the OrganizationProfile component to use hash-based routing by setting the "routing" prop to "hash".

Thanks for the information, this will help us better understand why this is happening. Seems like you are still blocked which is very unfortunate. We have prioritized this issue and we are expecting a fix soon.

Maybe for you to get unblocked you could simply use routing="hash" (with no path) for now.

@panteliselef Thanks a lot for the help. Yeah, sure, I can use the hash workaround for now, but please keep me updated once it's fixed

@oferitz this got closed automatically, we are still tracking the 2nd issue you reported, but since the original one is fixed I would keep this closed.

Try updating to latest @clerk/nextjs and removing the clerkJSVersion prop.

@panteliselef Thanks for the update. Is there a way for me to track the 2nd issue?

Hello @oferitz , I'm currently on the issue and I will try to share more details once my investigation is over.
I do have a few ideas about potential edge cases that are related to the way we detect if a route is a catch-all route or not. Could you please share your middleware code?

I'm very interested to find out how you're protecting your routes as this might be connected and it will certainly help me debug :)

@nikosdouvlis Sure.

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

const isMainRoute = createRouteMatcher(['/main(.*)'])

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

export const config = {
  matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)']
}

@oferitz Thank you very much, this does confirm my initial findings. The problem you're hitting is caused by the auth().protect() logic in your middleware. Let me give you some context:

All Clerk components can navigate through their own pages using 3 different strategies:

  • path routing: the components will modify the path of the URL that comes after the path they are mounted. Example: if you mount your org profile on /main/organization, the component will use /main/organization as its main route but it will use /main/organization/settings to display the settings page. In this case, we need a catch-all route so the /settings part can be handled by the Clerk component. This is the default for Clerk components in Nextjs apps.
  • hash routing: this only modifies just the fragment (the part of the URL that comes after #). This does not need a catch-all route because changing the fragment does not need to trigger a NextJS navigation
  • in memory routing: mainly used by modals, does not affect the URL at all but its not bookmarkable -refreshing the page will cause the component to lose its state

When you mount a path-based Clerk component, we try to check whether you are using a catch-all route or not. The only way to do this in NextJS right now is to fire a request to a random URL under the path the component is mounted (eg /main/organization/clerk_catchall_route_check_123123) and see whether this returns a 404 or not.

In your case, a race condition after signing out triggers the error as the component incorrectly fires the catch_all_check request I described above, but this fails because auth().protect() will block the route as you are signed out.

We're sorry for the trouble! This would be very hard to debug on your own. I'm working on updating the detection logic so this false-positive cannot happen again

@nikosdouvlis Thanks for the thorough explanation. I was actually wondering how you do this check, so thanks for sharing this info. I appreciate the effort. Please keep me posted once it's fixed.