LuckyPennySoftware/AutoMapper · error · AutoMapperConfigurationException
CreateProjection works with ProjectTo, not with Map.
Error message
CreateProjection works with ProjectTo, not with Map.
What it means
A map created with CreateProjection<TSrc,TDest> has TypeMap.Projection = true and is compiled only as a LINQ expression for ProjectTo; it has no runtime Map strategy. TypeMap.CheckProjection throws AutoMapperConfigurationException (with the missing-map inner exception) the moment such a map is selected for an in-memory Map. The message states the rule directly.
Source
Thrown at src/AutoMapper/TypeMap.cs:57
}
sourceMembers.Clear();
var propertyType = destinationProperty.GetMemberType();
if (profile.MapDestinationPropertyToSource(SourceTypeDetails, destinationType, propertyType, destinationName, sourceMembers, isReverseMap))
{
AddPropertyMap(destinationProperty, propertyType, sourceMembers);
}
}
}
public string CheckRecord() => ConstructorMap?.Ctor is ConstructorInfo ctor && ctor.IsFamily && ctor.Has<CompilerGeneratedAttribute>() ?
" When mapping to records, consider using only public constructors. See https://docs.automapper.io/en/latest/Construction.html." : null;
public Features<IRuntimeFeature> Features => Details.Features;
private TypeMapDetails Details => _details ??= new();
public bool HasDetails => _details != null;
public void CheckProjection()
{
if (Projection)
{
throw new AutoMapperConfigurationException("CreateProjection works with ProjectTo, not with Map.", MissingMapException(Types));
}
}
public static Exception MissingMapException(TypePair types) => MissingMapException(types.SourceType, types.DestinationType);
public static Exception MissingMapException(Type sourceType, Type destinationType)
=> new InvalidOperationException($"Missing map from {sourceType} to {destinationType}. Create using CreateMap<{sourceType.Name}, {destinationType.Name}>.");
public bool Projection { get; set; }
public LambdaExpression MapExpression { get; private set; }
public Expression Invoke(Expression source, Expression destination) =>
Expression.Invoke(MapExpression, ToType(source, SourceType), ToType(destination, DestinationType), ContextParameter);
internal bool CanConstructorMap() => Profile.ConstructorMappingEnabled && !DestinationType.IsAbstract &&
!CustomConstruction && !HasTypeConverter && DestinationConstructors.Length > 0;
public TypePair Types;
public ConstructorMap ConstructorMap { get; set; }
public TypeDetails SourceTypeDetails { get; private set; }
public TypeDetails DestinationTypeDetails { get; private set; }
public Type SourceType => Types.SourceType;
public Type DestinationType => Types.DestinationType;
public ProfileMap Profile { get; }View on GitHub (pinned to dfa6dd587c)
Solutions
- Change CreateProjection<TSrc,TDest> to CreateMap<TSrc,TDest> (or add a separate CreateMap) when in-memory Map is also needed.
- Keep the projection for EF queries and use ProjectTo exclusively for that DTO.
- Split into two DTOs/maps: a query projection DTO (ProjectTo) and a command DTO (CreateMap).
- If inheritance is involved, make the base map a CreateMap, not a CreateProjection.
Example fix
// before var config = new MapperConfiguration(c => c.CreateProjection<Order, OrderDto>()); var dto = mapper.Map<OrderDto>(order); // throws: projection not for Map // after var config = new MapperConfiguration(c => c.CreateMap<Order, OrderDto>()); var dto = mapper.Map<OrderDto>(order);
Defensive patterns
Strategy: validation
Validate before calling
var typeMap = mapper.ConfigurationProvider.FindTypeMapFor<TSource, TDestination>();
if (typeMap is { Projection: true })
{
throw new InvalidOperationException(
$"{typeof(TSource)}->{typeof(TDestination)} is a projection; use ProjectTo, or switch to CreateMap.");
}
var dest = mapper.Map<TDestination>(source); Type guard
static bool IsInMemoryMappable(IConfigurationProvider cfg, Type src, Type dst) =>
cfg.FindTypeMapFor(src, dst) is { } tm && !tm.Projection; Try / catch
try { dest = mapper.Map<TDestination>(source); }
catch (AutoMapperConfigurationException ex) when (ex.Message.Contains("CreateProjection works with ProjectTo"))
{
// switch the map to CreateMap, or call ProjectTo instead, then retry
} Prevention
- Use CreateProjection only for query/read DTOs served by ProjectTo; use CreateMap for anything mapped in memory.
- Keep query DTOs and command DTOs separate so a projection is never reused for Map.
- When inheriting, ensure base maps are CreateMap if any derived map is used in-memory.
When it happens
Trigger: cfg.CreateProjection<A,B>() followed by mapper.Map<B>(anA). Also when an inheritance chain or Include pulls a projection-only map into an in-memory mapping path.
Common situations: Reusing a query/read-model DTO for command-side in-memory mapping; copy-pasting CreateProjection where CreateMap was intended; mixing ProjectTo query DTOs with Map-based handlers; inheritance base defined as a projection.
Related errors
- Error building queryable mapping strategy.
- Error building constructor projection strategy.
- Duplicate CreateMap calls: {error.Types.SourceType.FullName}
- The type {typeMap.DestinationType.Name} does not have a cons
- {typeMap.DestinationType.Name} does not have a matching cons
AI-assisted analysis of LuckyPennySoftware/AutoMapper@dfa6dd587c (2026-08-13).
Data as JSON: /api/errors/de0a2141aba1b1ca.
Report an issue: GitHub.