Troubleshooting
Common errors, their causes, and how to fix them.
On this page
- Inertia.Render(...) does not compile
- My 404 shows the Inertia modal in production
- Form submissions fail with 419
- The Inertia page component was not found
- A redirect to another site turns into a 409
- The root view resolves outside the content root
- The page keeps reloading, or a request loops with 409
- A blank page at localhost:5173
- The dev server tags do not appear
- The root template was not found, or @inertia is missing
- Flash data or errors disappear
- Validation errors do not show up (Minimal API)
- Validation keys do not match my field names
- InertiaProp values can only be serialized by Inertia.Net
- Native AOT: JsonTypeInfo metadata was not provided
- SSR does not run and there is no error
- SSR skipped (circuit-open) or (overloaded)
- Several managed instances never become healthy
- The SSR output is not hydrated
- Does it work behind a path base?
- Can I use Newtonsoft.Json?
- Can I mix Razor Pages or views with Inertia pages?
- Forms submit invalid data
- 419 in tests
- Precognition: login locks users out while typing
- 'Location' is a type but is used like a variable
- A cached prop loader runs every request
Inertia.Render(...) does not compile
'Inertia' is a namespace. Call Render(...) unqualified. The namespace Inertia.Net shadows the class name outside the Inertia.Net namespace.
The global usings come from the package's buildTransitive props and need <ImplicitUsings>enable</ImplicitUsings>. Without implicit usings, or with InertiaNetImplicitUsings set to false, add using static Inertia.Net.Inertia;.
Generated code fails with 'Inertia' is a type
Inside a namespace that starts with Inertia.Net., the name Inertia binds to the Inertia.Net.Inertia class. Source generators that emit non-global:: names starting with Inertia then break. Do not put your app's code under an Inertia.Net.* namespace.
Vite manifest not found. Looked in: ...
Run your package manager’s build script (npm run build, bun run build, pnpm run build or yarn run build), or start the dev server so the hot file exists in Development, or set o.Vite.ManifestPath / o.Vite.BuildDirectory. Check that Vite writes .vite/manifest.json (build.manifest: true, Vite 5 and later) to wwwroot/build, and that the build output is part of what you deploy.
Vite entry '...' was not found in the manifest
The @vite("...") argument must equal the manifest key, which is the path relative to the Vite root. The error lists the available entries.
My 404 shows the Inertia modal in production
Register AddInertiaErrorPages() and call UseInertiaErrorPages() before UseInertia(). Create your error components; see Error pages.
Form submissions fail with 419
UseInertiaAntiforgery() rejected a request without a valid X-XSRF-TOKEN. Check that:
- the page was loaded through the middleware (it sets the
XSRF-TOKENcookie); - the cookie reaches the browser (
Secureon plain HTTP is dropped; see Deploying); - all instances share the Data Protection key ring;
UseAuthentication()runs before it.
A sign-in or sign-out changes the token: reload the page, or return Back() from OnRejected. Exempt webhooks and token-authenticated APIs with .DisableAntiforgery() or ShouldValidate.
The Inertia page component was not found
o.Pages.Directory is set and the component passed to Render(...) has no page file there. Names match exactly, as the client's page resolver does, even on case-insensitive file systems; the message suggests the closest page. Fix the name, or check Pages.Extensions when your pages use another extension. The check never runs outside Development.
A redirect to another site turns into a 409
That is ConvertExternalRedirects (on by default): an Inertia request cannot follow a redirect to another origin, so it gets 409 + X-Inertia-Location and the client does a full visit.
If it happens for your own host, the app is behind a proxy that does not forward the scheme (see Deploying), or use relative redirects. o.ConvertExternalRedirects = false disables the conversion.
The root view resolves outside the content root
RootView and WithRootView(...) take a path relative to the content root; absolute paths and .. are refused. Never derive them from request data.
The page keeps reloading, or a request loops with 409
A 409 means the asset version differs between the client and the server. A loop means it never converges: usually the version changes on every request (a VersionResolver that returns a new value each time, or a manifest regenerated by a file watcher), or different instances behind a load balancer serve different builds. Use a stable version string.
In Development the manifest hash changes after each vite build, which is expected once.
A blank page at localhost:5173
http://localhost:5173 is the Vite dev server. It serves assets and hot-reload updates to your app and has no pages of its own, so it shows a blank page. Open the ASP.NET Core URL printed by dotnet watch or dotnet run instead (http://localhost:5180 in the starter templates).
The dev server tags do not appear
The hot file is read only when ASPNETCORE_ENVIRONMENT=Development. Check that wwwroot/hot exists and contains http://localhost:5173, or set o.Vite.DevServerUrl. Remember base: '/' for the dev server in vite.config.
The root template was not found, or @inertia is missing
RootView is relative to the content root. Copy app.html to the output and publish directories (CopyToOutputDirectory, CopyToPublishDirectory). The template must contain @inertia.
Flash data or errors disappear
The state cookie is limited to about 4 KB by browsers. Flash less data, or use o.State.UseSession(). If the app runs on several instances, share Data Protection keys.
If the cookie is never sent back, check the cookie Secure flag on HTTP behind a proxy that does not forward X-Forwarded-Proto.
Validation errors do not show up (Minimal API)
Use .WithInertiaValidation() on the endpoint. Without it, the built-in AddValidation() filter returns a 400 problem details that the Inertia client cannot use. Make sure the model types are public.
Validation keys do not match my field names
Keys use dotted indices and available JSON member aliases. Minimal API and MVC use the page serializer’s HTTP JSON options and FastEndpoints its binding serializer policy; keep these aligned with client field names. Metadata-free or custom resolver paths can fall back to naming-policy conversion. See Validation.
InertiaProp values can only be serialized by Inertia.Net
An InertiaProp ended up somewhere Inertia.Net does not walk: a Flash(...) value, a read-only dictionary type that does not implement IDictionary, or a type with a custom converter.
Lists, typed dictionaries (Dictionary<TKey, TValue> with any key type), object members and IDictionary<string, object?> are walked. Put props in InertiaProps, or declare the member as InertiaProp (or object) on the typed page object.
Native AOT: JsonTypeInfo metadata was not provided
Add the type to your JsonSerializerContext and chain it with ConfigureHttpJsonOptions. Setting JsonSerializerIsReflectionEnabledByDefault=false in regular builds finds these early. See Native AOT.
SSR does not run and there is no error
SSR falls back to client rendering on any failure. Look for the warnings Inertia SSR failed (...) and Inertia SSR skipped (...). The first includes the server's hint.
Check o.Ssr.Mode, Urls, that the Node server is running (GET /health; with UseNodeProcess() look for Inertia.Net.Ssr log lines), that BundlePath points at the built file, that the path is not in ExcludePaths, and that the page is under MaxPageBytes.
Set Mode = SsrMode.Required in Development to surface the failure.
SSR skipped (circuit-open) or (overloaded)
circuit-open: the server failedCircuitBreaker.FailureThresholdtimes in a row and is skipped forBreakDuration; the earlierInertia SSR failedwarnings say why.overloaded: more renders arrived thanConcurrency.MaxActiveplusMaxQueued, or one waited longer thanQueueTimeout. Add servers or instances, or raise the limits if the server has headroom.
Several managed instances never become healthy
Every instance must listen on its own port: the SSR entry has to read process.env.INERTIA_SSR_PORT. A bundle built only by @inertiajs/vite listens on its baked-in port.
The SSR output is not hydrated
Use the hydrating client entry shown in Server-side rendering and build both bundles from the same sources. The SSR body is marked data-server-rendered="true".
Does it work behind a path base?
Yes. The page url keeps the path base, the Vite URLs include it, and Back() works.
Can I use Newtonsoft.Json?
No. Props use System.Text.Json with the HTTP JSON options.
Can I mix Razor Pages or views with Inertia pages?
Yes. Inertia handles only the endpoints that return Render(...). The middleware leaves other responses alone, apart from Vary: X-Inertia.
Forms submit invalid data
Minimal API endpoints need .WithInertiaValidation() or .WithInertiaPrecognition() and AddValidation(). Endpoint construction throws when validation resolvers are missing; routing can construct endpoints lazily. See Validation.
419 in tests
Use a cookie-preserving client, load an Inertia page first, then use request.WithXsrfToken(pageResponse) on the form request. See Testing.
Precognition: login locks users out while typing
Precognitive POSTs pass through rate limiting. Use separate bounded validation and submission partitions and validate on blur; see Precognition. A throttled validation still rejects, so show a retry message. To render a custom Inertia error page for a real submission rejected with 429, add 429 with AddInertiaErrorPages(o => o.StatusCodes.Add(429)) and put UseInertiaErrorPages() before UseRateLimiter(). Precognitive JSON responses keep their 429 and Precognition: true header.
'Location' is a type but is used like a variable
The imported static Location name may collide with a type. Call Inertia.Net.Inertia.Location(url) explicitly, or opt out with <InertiaNetImplicitUsings>false</InertiaNetImplicitUsings> and add your own imports. See Configuration.
A cached prop loader runs every request
Check that the key and duration are stable and the cache store works. Dot-key props support caching; include every user or tenant dependency in the cache key. A stale refresh needs its own scope and may be dropped when the background queue is full. See Props.