In an Angular app, a route never renders or a redirect keeps looping; how do you diagnose the route configuration?
answer
- read the dev-mode config errors
- look for shadowing earlier routes
- 'Cannot match any routes' names the segment
- absolute redirects restart matching
- trace router events while navigating
basics
~20 sCheck the console for the router's dev-mode configuration errors, look for earlier routes shadowing the missing one (a ** or :param too high), check pathMatch on parents and redirects, and trace navigations with withDebugTracing() to see which route matched or why matching failed.
solid answer
~50 sFirst the console: in development the router validates its configuration when it receives it and throws precise errors — a path starting with a slash, an `undefined` entry from a stray comma, `redirectTo` combined with `component` or `canActivate`, an empty-path redirect without `pathMatch`. At navigation time, `NG04002` "Cannot match any routes. URL Segment: ..." names the segment nothing matched, and `NG04016` flags a possible infinite loop of absolute redirects. Then read the table in order: first match wins, so a `**` or `:slug` route above the missing one shadows it, and `pathMatch: 'full'` on a parent blocks its children. Relative redirects are not re-redirected at the same level, so a chain of two relative redirects ends in no match. Finally, `provideRouter(routes, withDebugTracing())` logs every router event, showing which configuration was recognised or where navigation errored.
code
ts · 8 linesimport { ApplicationConfig } from '@angular/core';
import { provideRouter, withDebugTracing } from '@angular/router';
import { routes } from './app.routes';
// Temporary: logs every router event (NavigationStart, RoutesRecognized, NavigationError, ...)
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes, withDebugTracing())],
};go deeper
Recall that route order matters, paths have no leading slash, and the ** route belongs last.
Explain the dev-mode configuration errors and the NG04002 and NG04016 navigation errors, and what each one points to.
Diagnose shadowing, parent pathMatch mistakes and redirect chains systematically, confirm with router tracing, and lock the fix in with a route test.
Establish route-table conventions and tests so large teams can add routes without shadowing each other's URLs.
## Start with what the router already tells you In **development builds** Angular's router validates the route configuration when it receives it and throws `NG04014` errors with a readable message. The common ones: - "path cannot start with a slash" — paths are relative: `'posts'`, not `'/posts'`. - "Encountered undefined route. The reason might be an extra comma" — `,,` left an `undefined` hole in the array. - "redirectTo and component/loadComponent cannot be used together" — a route either renders or redirects. - "redirectTo and canActivate cannot be used together. Redirects happen before guards are executed." - "please provide 'pathMatch'" — an empty-path redirect without `pathMatch`. - "One of the following must be provided: component, loadComponent, redirectTo, children or loadChildren". These checks are skipped in production builds, so reproduce configuration bugs in a development build before anything else. ## Errors at navigation time | Symptom | Likely cause | |---|---| | `NG04002` "Cannot match any routes. URL Segment: '...'" | No route (and no `**`) consumes that segment; or a chained relative redirect left segments nothing matches | | `NG04016` "Detected possible infinite redirect" (dev) | Absolute string redirects bouncing between URLs; reported after the router's redirect limit | | `NG04001` "Cannot redirect to '...'. Cannot find ':x'" | A `redirectTo` string uses a placeholder the redirect route's path does not define | | The wrong component renders, no error | An earlier route matches first | | The 404 page renders for a valid URL | The `**` route is not last, or the intended route cannot consume the whole URL | ## Reading the table like the router does 1. **Order.** The router walks each routes array top to bottom and takes the first route that matches. Look above the missing route for `**`, a parameter route of the same shape (`posts/:slug` above `posts/new`), or an empty-path route with children that captures everything. 2. **Whole-URL rule.** A route without children must consume all remaining segments. `posts/:slug` does not match `/posts/2026/hello`; a `:param` is one segment. 3. **`pathMatch` on parents.** `'full'` on a route with children means it only matches when nothing follows, so its children never see `/posts/hello`. 4. **Redirect semantics.** A relative redirect replaces the consumed segments and re-matches at the same level with redirects **disabled**, so `a -> b` followed by `b -> c` at one level does not reach `c`; that navigation fails to match. An absolute redirect (leading slash, or a `UrlTree` from a function) restarts matching from the root, which is how loops arise. 5. **Backtracking.** When a parent matches but none of its children can consume the rest, the router abandons that parent and tries the next sibling — a component route may silently lose to a later one. ## Tracing a navigation `provideRouter(routes, withDebugTracing())` logs every router event to the console. For a failing navigation you see where it stops: a `NavigationError` with the matching error, a `NavigationCancel` from a guard, or a `RoutesRecognized` whose state shows which configuration was actually chosen. The feature only logs in development builds — in production it provides nothing — but remove it once the bug is found, because the event noise buries other console output. ## A worked diagnosis A team adds `{ path: 'posts/new', component: NewPost }` at the end of the routes, after `posts/:slug` and `**`. `/posts/new` renders `PostDetail` with `slug = 'new'`, and nothing errors. The tracing output shows `RoutesRecognized` with the `posts/:slug` configuration. The fix is to move `posts/new` above `posts/:slug`, and to keep `**` as the final entry. ## Prevent it next time - Keep a **route-table test**: navigate the router to representative URLs and assert which component renders. - Order each array: empty-path redirects, static paths, parameter paths, then `**`. - Use `pathMatch: 'full'` on empty-path and exact-URL redirects only. - Prefer relative paths and one redirect hop; chained redirects are hard to reason about.
- Why might a route configuration error appear locally but not in production?The router's configuration validation runs only in development builds. Production skips it, so a bad table does not throw at startup; it simply fails to match or behaves oddly at navigation time. Reproduce routing bugs in a development build to get the precise messages.
- How would you stop route-order regressions from coming back?Add a small test that navigates the router to key URLs — `/`, `/posts`, `/posts/new`, `/posts/some-slug`, an unknown URL — and asserts which component renders. A reordered or newly added route that shadows another then fails the test instead of reaching users.
saying these in an interview costs you the question
- The router validates route configuration in production builds too
- A relative redirect can chain into another redirect at the same level
- Cannot match any routes always means the route is missing
- Absolute and relative redirects restart matching the same way
- withDebugTracing logs router events in production builds too