Next.js Middleware Error: middleware.ts failed to compile or execute
MiddlewareError — middleware compilation or runtime failure
Verified against Next.js 14.2 docs: Middleware, Next.js 15.0 docs: Edge Runtime API reference, Vercel docs: Edge Middleware limitations · Updated June 2026
> quick_fix
Middleware runs in the Edge Runtime, which does not support Node.js APIs like fs, path, crypto (full), or Buffer. Remove Node.js imports, use only Web APIs (fetch, Response, URL, crypto.subtle), fix your matcher config syntax, and check for redirect loops where middleware redirects to a path that also triggers the middleware.
// middleware.ts — must be in the project root (next to app/)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
if (!request.cookies.get('session') && pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*'],
};What causes this error
Next.js middleware runs in the Edge Runtime — a lightweight V8-based environment that supports Web APIs but not the full Node.js runtime. When middleware.ts imports a Node.js-specific module (fs, path, net, child_process, or node: prefixed imports), the Edge Runtime compiler rejects it at build time. At runtime, common failures include infinite redirect loops (middleware redirects to a path that matches its own matcher), incorrect matcher patterns (the route never triggers or triggers on every request including _next/static), and errors from accessing request properties incorrectly. The middleware file must also be placed in the correct location — the project root alongside the app or pages directory, not inside app/.
How to fix it
- 01
step 1
Fix Node.js import errors in Edge Runtime
The Edge Runtime does not support Node.js built-in modules. If your middleware imports a library that depends on fs, path, crypto (the full Node.js version), or any other built-in, the build fails. Replace with Edge-compatible alternatives or use the Web Crypto API.
// BAD: Node.js crypto — fails in Edge Runtime // import crypto from 'crypto'; // const hash = crypto.createHash('sha256').update(token).digest('hex'); // GOOD: Web Crypto API — works in Edge Runtime const encoder = new TextEncoder(); const data = encoder.encode(token); const hashBuffer = await crypto.subtle.digest('SHA-256', data); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hash = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); - 02
step 2
Fix the middleware file location
middleware.ts must be in the project root — the same level as your app/ or pages/ directory. If it is inside app/middleware.ts or src/app/middleware.ts, Next.js will not recognize it. For projects using the src/ directory, place it at src/middleware.ts.
# Correct locations: # project-root/middleware.ts (no src/ directory) # project-root/src/middleware.ts (with src/ directory) # WRONG locations (will be silently ignored): # project-root/app/middleware.ts # project-root/pages/middleware.ts - 03
step 3
Fix the matcher configuration
The matcher determines which routes trigger the middleware. Common mistakes: missing the leading slash, using regex syntax instead of Next.js path patterns, or forgetting to exclude static assets. An overly broad matcher that catches _next/static or favicon.ico causes asset loading failures.
export const config = { matcher: [ // Match all routes except static files, images, and favicon '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)', ], }; // Or explicitly list protected routes: export const config = { matcher: ['/dashboard/:path*', '/api/protected/:path*'], }; - 04
step 4
Fix infinite redirect loops
If middleware redirects to /login and /login also matches the middleware matcher, the browser hits a redirect loop (ERR_TOO_MANY_REDIRECTS). Either exclude the redirect target from the matcher, or add a condition to skip middleware on the login page.
import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { const { pathname } = request.nextUrl; // Skip middleware for login page to avoid redirect loop if (pathname === '/login') { return NextResponse.next(); } const session = request.cookies.get('session'); if (!session) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); } export const config = { matcher: ['/dashboard/:path*', '/settings/:path*'], }; - 05
step 5
Debug middleware execution
Middleware errors can be hard to trace because they run before the route handler. Use console.log in development — middleware logs appear in the terminal (not the browser console). In production, check your hosting provider's function logs.
export function middleware(request: NextRequest) { console.log('Middleware hit:', request.nextUrl.pathname); console.log('Cookies:', request.cookies.getAll()); console.log('Headers:', Object.fromEntries(request.headers)); // Your middleware logic here return NextResponse.next(); }
How to verify the fix
- npm run build completes without middleware compilation errors.
- Protected routes redirect to login when no session cookie is present.
- The login page loads without an infinite redirect loop.
- Static assets (_next/static, images, favicon) load correctly and are not intercepted by middleware.
Why MiddlewareError happens at the runtime level
Next.js middleware compiles separately from the rest of the application, targeting the Edge Runtime rather than Node.js. The Edge Runtime is a stripped-down JavaScript environment based on V8 isolates — it provides Web Standards APIs (Request, Response, fetch, crypto.subtle, TextEncoder) but intentionally excludes Node.js built-in modules for security and performance. When the webpack compiler encounters a require('fs') or import from 'node:path' in middleware code, it cannot resolve the module in the Edge environment and throws a build-time error. Runtime failures occur when the compiled middleware encounters issues like redirect loops or malformed URLs that the compiler could not catch statically.
Common debug mistakes for MiddlewareError
- Importing a utility file that works fine in Server Components but uses Node.js APIs internally — the import compiles for Edge Runtime and fails because the dependency tree contains fs or path references.
- Placing middleware.ts inside the app/ directory instead of the project root — Next.js silently ignores it and the middleware never runs, which looks like the middleware is broken when it was never found.
- Creating a redirect loop by redirecting unauthenticated users to /login while the matcher includes /login — the browser shows ERR_TOO_MANY_REDIRECTS and no page loads.
- Using request.body in middleware — the Edge Runtime does not support reading the request body in middleware by default; body parsing must happen in route handlers.
- Not returning NextResponse.next() for requests that should pass through — forgetting the default return means matched routes get no response.
When MiddlewareError signals a deeper problem
Middleware's Edge Runtime constraint is the most frequent source of confusion because it creates a hidden third execution environment in Next.js applications. Developers already navigate Server Components (Node.js) and Client Components (browser). Middleware adds a third context (Edge) with its own set of allowed APIs, and the boundaries between these three environments are not visible in the code. A utility function that works perfectly in a Server Component will crash middleware if it imports a single Node.js built-in. The real solution is to keep middleware thin — authentication token checks, redirects, rewrites — and push all complex logic into API routes or Server Components where the full Node.js runtime is available.
Editor's take
Middleware is the most powerful and the most misused feature in the Next.js App Router. It runs before every matched request, which means it can do authentication, geolocation redirects, A/B testing, and feature flags before any page code executes. But that power comes with a sharp constraint: it runs in the Edge Runtime, not Node.js.
The Edge Runtime is fast because it is limited. No filesystem access, no native Node.js modules, no Prisma, no heavy dependencies. When developers try to do too much in middleware — database lookups, complex business logic, PDF generation — they hit compiler errors that feel arbitrary. The error message says 'Module not found: Can\'t resolve fs' and the developer thinks the build is broken. It is not broken. The middleware is doing work it was never designed to do.
The cleanest middleware files are under 30 lines. They check a cookie or header, make a routing decision (redirect, rewrite, or pass through), and return. Everything else belongs downstream. If you need to validate a session against a database, verify a JWT signature in middleware (Web Crypto API handles this) and do the full session lookup in the Server Component that renders the page.
The infinite redirect loop is the other classic trap. Middleware redirects /dashboard to /login, but /login also matches the middleware matcher, so middleware redirects /login to /login forever. The fix is always the same: exclude redirect targets from the matcher, or add an early return for paths that should not be processed. Every middleware file should have an explicit list of paths it skips — not just paths it matches.
By Bikram Nath · Curator · Updated June 2026
Frequently asked questions
Can I use a database client like Prisma in middleware?
No. Prisma uses Node.js APIs internally and cannot run in the Edge Runtime. If your middleware needs to validate a session against a database, use a JWT-based approach where middleware only verifies the token signature (using Web Crypto), or call an API route from middleware using fetch. Some ORMs like Drizzle offer Edge-compatible drivers (like drizzle-orm/neon-http), but Prisma's core client is Node.js-only.
Why does my middleware run on every request including images and CSS?
Without a matcher config, middleware runs on every request — including _next/static, _next/image, and favicon.ico. Always add a matcher to scope your middleware. The most common pattern is a negative lookahead regex that excludes static assets: '/((?!_next/static|_next/image|favicon.ico).*)'.
Can I have multiple middleware files for different routes?
No. Next.js only supports a single middleware.ts file per project. To handle different logic for different routes, use conditional checks on request.nextUrl.pathname inside your single middleware function. You can organize the logic by importing helper functions from other files, but the entry point must be one middleware.ts file.