Troubleshooting

Common errors, their causes, and how to fix them.

On this page

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-TOKEN cookie);
  • the cookie reaches the browser (Secure on 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 failed CircuitBreaker.FailureThreshold times in a row and is skipped for BreakDuration; the earlier Inertia SSR failed warnings say why.
  • overloaded: more renders arrived than Concurrency.MaxActive plus MaxQueued, or one waited longer than QueueTimeout. 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.