diff --git a/test/e2e/app-dir/instant-validation/app/default/invalid-use-pathname-no-samples/[slug]/page.tsx b/test/e2e/app-dir/instant-validation/app/default/invalid-use-pathname-no-samples/[slug]/page.tsx
new file mode 100644
index 000000000000..49fdf62dc6c5
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation/app/default/invalid-use-pathname-no-samples/[slug]/page.tsx
@@ -0,0 +1,9 @@
+import { PathnameLabel } from './pathname-label'
+
+export default function Page() {
+ return (
+
+
+
+ )
+}
diff --git a/test/e2e/app-dir/instant-validation/app/default/invalid-use-pathname-no-samples/[slug]/pathname-label.tsx b/test/e2e/app-dir/instant-validation/app/default/invalid-use-pathname-no-samples/[slug]/pathname-label.tsx
new file mode 100644
index 000000000000..8c4c2520c33c
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation/app/default/invalid-use-pathname-no-samples/[slug]/pathname-label.tsx
@@ -0,0 +1,8 @@
+'use client'
+
+import { usePathname } from 'next/navigation'
+
+export function PathnameLabel() {
+ const pathname = usePathname()
+ return {pathname}
+}
diff --git a/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segment-no-samples/[slug]/active-tab.tsx b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segment-no-samples/[slug]/active-tab.tsx
new file mode 100644
index 000000000000..942eb48c416d
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segment-no-samples/[slug]/active-tab.tsx
@@ -0,0 +1,8 @@
+'use client'
+
+import { useSelectedLayoutSegment } from 'next/navigation'
+
+export function ActiveTab() {
+ const segment = useSelectedLayoutSegment()
+ return {segment}
+}
diff --git a/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segment-no-samples/[slug]/layout.tsx b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segment-no-samples/[slug]/layout.tsx
new file mode 100644
index 000000000000..8f704aba0004
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segment-no-samples/[slug]/layout.tsx
@@ -0,0 +1,10 @@
+import { ActiveTab } from './active-tab'
+
+export default function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+ )
+}
diff --git a/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segment-no-samples/[slug]/page.tsx b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segment-no-samples/[slug]/page.tsx
new file mode 100644
index 000000000000..1e5a065b23c7
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segment-no-samples/[slug]/page.tsx
@@ -0,0 +1,3 @@
+export default function Page() {
+ return Leaf page
+}
diff --git a/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segments-no-samples/[slug]/breadcrumb-trail.tsx b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segments-no-samples/[slug]/breadcrumb-trail.tsx
new file mode 100644
index 000000000000..97a6f5aa98a4
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segments-no-samples/[slug]/breadcrumb-trail.tsx
@@ -0,0 +1,8 @@
+'use client'
+
+import { useSelectedLayoutSegments } from 'next/navigation'
+
+export function BreadcrumbTrail() {
+ const segments = useSelectedLayoutSegments()
+ return {segments.join('/')}
+}
diff --git a/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segments-no-samples/[slug]/layout.tsx b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segments-no-samples/[slug]/layout.tsx
new file mode 100644
index 000000000000..9d12f129b129
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segments-no-samples/[slug]/layout.tsx
@@ -0,0 +1,10 @@
+import { BreadcrumbTrail } from './breadcrumb-trail'
+
+export default function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ {children}
+
+ )
+}
diff --git a/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segments-no-samples/[slug]/page.tsx b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segments-no-samples/[slug]/page.tsx
new file mode 100644
index 000000000000..1e5a065b23c7
--- /dev/null
+++ b/test/e2e/app-dir/instant-validation/app/default/invalid-use-selected-layout-segments-no-samples/[slug]/page.tsx
@@ -0,0 +1,3 @@
+export default function Page() {
+ return Leaf page
+}
diff --git a/test/e2e/app-dir/instant-validation/app/default/page.tsx b/test/e2e/app-dir/instant-validation/app/default/page.tsx
index 92e5ef89941d..f13c926ffd58 100644
--- a/test/e2e/app-dir/instant-validation/app/default/page.tsx
+++ b/test/e2e/app-dir/instant-validation/app/default/page.tsx
@@ -14,6 +14,15 @@ export default async function Page() {
+
+
+
+
+
+
+
+
+
)
diff --git a/test/e2e/app-dir/instant-validation/instant-validation.test.ts b/test/e2e/app-dir/instant-validation/instant-validation.test.ts
index a90a26734f79..942ed14fec8e 100644
--- a/test/e2e/app-dir/instant-validation/instant-validation.test.ts
+++ b/test/e2e/app-dir/instant-validation/instant-validation.test.ts
@@ -6,6 +6,7 @@ import {
waitForValidation,
} from 'e2e-utils/instant-validation'
import {
+ getRedboxSource,
openRedbox,
retry,
waitForNoErrorToast,
@@ -1893,6 +1894,48 @@ describe('instant validation', () => {
expectNoBuildValidationErrors(result)
}
})
+
+ // usePathname is misclassified as "uncached data" today and the dev
+ // overlay attributes the error to the parent's JSX line. This test
+ // asserts the correct stack attribution. When the URL-hook factory
+ // lands, `it.failing` will itself fail — flip to `it`.
+ /* eslint-disable jest/no-standalone-expect */
+ ;(isNextDev && !isClientNav ? it.failing : it)(
+ 'invalid - usePathname() in a client component on a route with a fallback param',
+ async () => {
+ if (!isNextDev || isClientNav) return
+ const browser = await navigateTo(
+ '/default/invalid-use-pathname-no-samples/123'
+ )
+ await openRedbox(browser)
+ const source = await getRedboxSource(browser)
+ expect(source).toContain('pathname-label.tsx')
+ expect(source).toContain('PathnameLabel')
+ }
+ )
+
+ it('invalid - useSelectedLayoutSegment() in a layout on a route with a fallback param', async () => {
+ if (!isNextDev || isClientNav) return
+ const browser = await navigateTo(
+ '/default/invalid-use-selected-layout-segment-no-samples/123'
+ )
+ await openRedbox(browser)
+ const source = await getRedboxSource(browser)
+ expect(source).toContain('active-tab.tsx')
+ expect(source).toContain('ActiveTab')
+ })
+
+ it('invalid - useSelectedLayoutSegments() in a layout on a route with a fallback param', async () => {
+ if (!isNextDev || isClientNav) return
+ const browser = await navigateTo(
+ '/default/invalid-use-selected-layout-segments-no-samples/123'
+ )
+ await openRedbox(browser)
+ const source = await getRedboxSource(browser)
+ expect(source).toContain('breadcrumb-trail.tsx')
+ expect(source).toContain('BreadcrumbTrail')
+ })
+ /* eslint-enable jest/no-standalone-expect */
})
describe('client errors', () => {