Unable to Resolve Service for Type in ASP.NET Core: Causes and Fixes

You start the API, hit any endpoint on a controller, and the request dies before a single line of your code runs:
System.InvalidOperationException: Unable to resolve service for type
'MyApp.Application.Interfaces.IOrderService' while attempting to activate
'MyApp.Api.Controllers.OrdersController'.
The unable to resolve service for type error is the ASP.NET Core dependency injection container telling you something very specific: a constructor asked for a type, and the container has no idea how to build it. In production I've seen this exact message stop a Friday deploy cold, and in almost every case the root cause was one of seven things. This article walks through all of them, with the fix for each and the diagnostic that tells you which one you are looking at. If you want the same DI wiring shown inside a complete, runnable API rather than in isolated snippets, the annotated source on Patreon has the whole container setup wired end to end.
The reason this error is so common is that registration and consumption live in two different files, and nothing checks that they agree until runtime. Getting that right in a real codebase means designing the interface, the implementation, and the registration as one unit. Chapter 3 of the Zero to Production course builds exactly that pairing - IProductRepository and its implementation, registered and consumed inside a working ASP.NET Core API - so the shape is obvious before it ever breaks.
Everything below is verified against .NET 10 and the built-in Microsoft.Extensions.DependencyInjection container.
What Does "Unable to Resolve Service for Type" Actually Mean?
It means the container was asked to construct a type, walked its constructor parameters, and hit one it has no registration for. The exception is thrown at activation time, not at startup, which is why the app boots fine and only fails when a request arrives.
Read the message as two halves:
Unable to resolve service for type 'X'- X is the missing registration. This is the thing you forgot.while attempting to activate 'Y'- Y is the consumer. This is where the request was made from.
So Unable to resolve service for type 'IOrderService' while attempting to activate 'OrdersController' means: OrdersController has a constructor parameter of type IOrderService, and nothing in Program.cs maps IOrderService to a concrete class.
One nuance that trips people up constantly: the type named in the message is not always the one you registered wrong. If IOrderService is registered but OrderService takes an IPricingEngine that is not, the container reports the failure against the type it could not build. Always read the whole chain before assuming the top-level service is the culprit.
Cause 1: The Service Was Never Registered
This is the overwhelming majority of real cases. The interface exists, the implementation exists, and nobody wired them together.
// Program.cs - the registration that was missing
builder.Services.AddScoped<IOrderService, OrderService>();
Pick the lifetime deliberately rather than defaulting to AddScoped out of habit. AddScoped gives one instance per HTTP request and is the right default for anything touching DbContext. AddSingleton is for stateless, thread-safe services. AddTransient creates a new instance on every injection. If that decision is not obvious to you yet, our DI lifetimes decision guide breaks down where each one belongs.
Fast diagnostic: search the solution for the interface name. If the only two hits are the interface declaration and the constructor parameter, you have found it.
Cause 2: You Registered the Concrete Type, Not the Interface
A subtle one, and the error message looks identical.
// Registers OrderService. Does NOT register IOrderService.
builder.Services.AddScoped<OrderService>();
The container now knows how to build OrderService, but the controller asks for IOrderService, and that mapping does not exist. The two-generic-argument overload is what creates the mapping:
builder.Services.AddScoped<IOrderService, OrderService>();
If you genuinely need both - some code injects the interface, other code injects the concrete class - register the mapping and then forward the concrete registration so you do not end up with two separate instances inside the same scope:
builder.Services.AddScoped<OrderService>();
builder.Services.AddScoped<IOrderService>(sp => sp.GetRequiredService<OrderService>());
Cause 3: A Dependency Deeper in the Chain Is Missing
The container resolves recursively. OrdersController needs IOrderService, OrderService needs IPricingEngine, PricingEngine needs ITaxProvider. Miss the registration at any level and activation fails.
The message points at the missing link, not at the controller. So if you see Unable to resolve service for type 'ITaxProvider' while attempting to activate 'PricingEngine', the fix is at the bottom of the chain even though the symptom appeared at the top.
Fast diagnostic: the type in attempting to activate tells you which constructor to open. Walk that constructor's parameters against your registrations, one at a time.
Cause 4: Registration Happens After the Container Is Built
Everything registered on builder.Services must be registered before builder.Build() is called. Anything after that point is either ignored or throws, because the service provider is already constructed.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IOrderService, OrderService>(); // correct - before Build()
var app = builder.Build();
// Any Services.Add* here is too late.
This surfaces most often when someone factors registrations into an extension method and then calls it in the wrong place, or when a conditional registration sits behind an if that runs after Build(). Keep every registration in one clearly named block near the top of Program.cs.
Cause 5: Open Generics Registered as Closed Types
Generic services need the open generic form. Registering a single closed type only satisfies that one closed type.
// Only ever resolves IRepository<Order>.
builder.Services.AddScoped<IRepository<Order>, Repository<Order>>();
// Resolves IRepository<T> for every T.
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
The second form uses the typeof overload with unbound generics. Miss it and you will get the error for every entity except the one you happened to register.
Cause 6: A Missing Project Reference in a Layered Solution
In Clean Architecture solutions the interface lives in one project and the implementation in another. If the API project does not reference the Infrastructure project, the registration line will not even compile - but teams work around that by moving registration into an extension method inside Infrastructure, and then forget to call it.
// Infrastructure/DependencyInjection.cs
public static IServiceCollection AddInfrastructure(this IServiceCollection services) =>
services.AddScoped<IOrderRepository, OrderRepository>();
// Program.cs - the call that was never added
builder.Services.AddInfrastructure();
Fast diagnostic: if your solution has an AddApplication() / AddInfrastructure() convention, check that every one of them is actually invoked in Program.cs. A missing call fails silently at startup and loudly on the first request.
Cause 7: Injecting a Scoped Service Into a Singleton
This one produces a different but closely related exception:
System.InvalidOperationException: Cannot consume scoped service
'MyApp.Data.AppDbContext' from singleton 'MyApp.Services.CacheWarmer'.
You have not forgotten a registration. You have created a captive dependency: a long-lived singleton holding a short-lived scoped instance, which would keep a DbContext alive for the life of the process. The container refuses on purpose.
The fix is to inject IServiceScopeFactory and create a scope where you need one:
public sealed class CacheWarmer(IServiceScopeFactory scopeFactory) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken ct)
{
using var scope = scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// use db inside this scope only
}
}
That primary-constructor syntax requires C# 12 or later. This scenario shows up constantly in background services, and the closely related root-provider variant is covered in depth in our post on cannot resolve scoped service from root provider.
How Do You Catch This Error at Startup Instead of at Runtime?
Turn on scope validation and eager validation so the container verifies every registration when the app starts rather than when a request arrives. In development this is on by default, but you have to opt in explicitly for other environments:
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true;
options.ValidateOnBuild = true;
});
ValidateOnBuild walks every registration at Build() time and throws immediately for anything it cannot construct. ValidateScopes catches captive dependencies. Together they convert a per-request 500 into a deterministic startup crash, which is exactly what you want in CI.
The trade-off is honest: ValidateOnBuild adds a small amount of startup time proportional to the number of registrations. On every API I've shipped, that cost has been worth it. A container error that fails the pipeline is free; the same error discovered by a customer is not.
For anything that cannot be validated at build time - factory registrations, conditional wiring - a single integration test that spins up WebApplicationFactory and resolves every controller is a cheap safety net.
Quick Reference: Symptom to Cause
| What the message says | Most likely cause |
|---|---|
| Type is your own interface, consumer is a controller | Never registered (Cause 1) |
| Type is an interface, concrete class is registered | Interface not mapped (Cause 2) |
| Consumer is a service, not a controller | Missing link deeper in the chain (Cause 3) |
| Registration clearly exists in the file | Registered after Build(), or extension method never called (Causes 4 and 6) |
Type is generic, for example IRepository<Invoice> |
Closed instead of open generic (Cause 5) |
| Message says "Cannot consume scoped service ... from singleton" | Captive dependency (Cause 7) |
Frequently Asked Questions
Why Does the App Start Fine and Only Fail When I Call an Endpoint?
Because controllers are activated per request, not at startup. The container does not attempt to construct OrdersController until a request routes to it, so a missing registration stays invisible until then. Enabling ValidateOnBuild moves the failure to startup, which is where you want it.
What Is the Difference Between AddScoped, AddSingleton and AddTransient Here?
Lifetime does not affect whether the service resolves, only how many instances exist. A missing registration throws regardless of which lifetime you would have used. Lifetime becomes relevant for the captive dependency variant of the error, where a singleton cannot legally hold a scoped service.
How Do I Fix "Unable to Resolve Service for Type DbContext"?
AddDbContext<AppDbContext>() registers the context as scoped by default. If the message names your DbContext, either the AddDbContext call is missing, or you are injecting the context into a singleton such as a BackgroundService or a hosted service. For the second case, inject IServiceScopeFactory and resolve the context inside a created scope.
Can I Use Autofac or Scrutor to Avoid Registering Every Service by Hand?
Yes. Assembly-scanning registration removes an entire class of this error by convention-registering every IFoo to its matching Foo. It also makes the wiring less explicit, which is its own trade-off. We compare the options in Autofac vs Scrutor vs Microsoft DI.
Why Does This Happen With Minimal APIs Too?
Minimal API endpoint handlers get their parameters from the same container. A handler parameter that is not a route value, query value or body is resolved as a service, so an unregistered type produces the same exception. Marking the parameter with [FromServices] makes the intent explicit and produces a clearer failure.
Does the Official Microsoft Documentation Cover This?
The dependency injection in ASP.NET Core guide documents the registration APIs, lifetimes and scope validation behaviour in detail, and is the authoritative reference for the container's resolution rules.
Wrapping Up
The unable to resolve service for type error is never mysterious once you read the message properly: the first type is what is missing, the second is who asked for it. Nine times out of ten the fix is a single line in Program.cs. The remaining cases are open generics, an uncalled registration extension method, or a captive dependency - and all three have a mechanical fix.
The durable improvement is not memorising the seven causes. It is turning on ValidateOnBuild and ValidateScopes so the container tells you about the problem in CI instead of in front of a user.
About the Author
I'm Celin Daniel, Co-founder of Coding Droplets. I've been building .NET and ASP.NET Core systems in production for 13+ years - APIs, distributed backends, enterprise platforms. Everything I write here comes from real shipping experience: patterns that held up, trade-offs that bit us, and lessons learned the hard way.
GitHub: codingdroplets
YouTube: Coding Droplets
Website: codingdroplets.com






