Skip to content

Fix: C# Cannot implicitly convert type 'X' to 'Y'

FixDevs · (Updated: )

Part of:  C# and .NET Errors

Quick Answer

How to fix C# cannot implicitly convert type error caused by type mismatches, nullable types, async return values, LINQ result types, and generic constraints.

Two Error Codes Hiding Behind One Message

I used to read every “Cannot implicitly convert” error the same way and reach for a cast, and about half the time the cast made things worse. What finally clicked for me was that there are two different errors wearing the same wording. CS0266 is the compiler asking “are you missing a cast?” and a cast is genuinely the answer. CS0029 is the compiler saying the types are unrelated, where a cast does nothing and you actually need to convert the value. Now I check the code number first, and the right fix is usually obvious from there.

You compile C# code and get:

error CS0029: Cannot implicitly convert type 'string' to 'int'

Or variations:

error CS0029: Cannot implicitly convert type 'int?' to 'int'
error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List<int>' to 'System.Collections.Generic.IEnumerable<string>'
error CS0266: Cannot implicitly convert type 'double' to 'float'. An explicit conversion exists (are you missing a cast?)
error CS0029: Cannot implicitly convert type 'Task<string>' to 'string'

You assigned or returned a value of one type where a different type is expected. C# does not allow implicit conversions between incompatible types, or between types where the conversion could lose data.

What the Compiler Refuses to Do Silently

The two error codes you see here are not the same problem, and telling them apart points you straight at the fix. CS0029 means no conversion exists at all; the types are unrelated, like string and int, and no cast will help; you need a conversion method or a different value. CS0266 means an explicit conversion exists but you didn’t write it; the compiler is literally telling you “are you missing a cast?” because the conversion is allowed but could lose data. Reach for a cast on CS0266; reach for Parse, Convert, await, or .ToList() on CS0029.

C# is a statically typed language. Every variable, parameter, and return value has a declared type. When you assign a value of one type to a variable of another type, the compiler checks if an implicit conversion exists. The guiding rule behind the whole system is safety without surprise: the compiler only performs a conversion silently when it is guaranteed to preserve the value. A wider numeric type can always hold a narrower one, and a derived type is always its base type, so those widen for free. Anything that could truncate, overflow, throw, or change meaning is refused until you opt in explicitly, which is what every fix below is really doing.

Implicit conversions are allowed when no data loss is possible:

int x = 42;
long y = x;     // OK — int fits in long
double z = x;   // OK — int fits in double

Implicit conversions are NOT allowed when data could be lost or types are incompatible:

double d = 3.14;
int i = d;          // Error — would lose decimal part
string s = 42;      // Error — int is not a string
int? n = 5;
int x = n;          // Error — nullable might be null

Common causes:

  • Wrong variable type. Assigning a string to an int, or vice versa.
  • Nullable type mismatch. Assigning int? to int without handling the null case.
  • Missing await. Using the result of an async method without await, getting Task<T> instead of T.
  • LINQ type mismatch. A LINQ query returns a different type than expected.
  • Narrowing numeric conversion. Assigning a double to a float, or a long to an int.
  • Generic type mismatch. Passing List<Derived> where List<Base> is expected.

Fix 1: Use Explicit Casting

When the compiler says an explicit conversion exists, add a cast:

double d = 3.14;
int i = (int)d;          // Explicit cast — truncates to 3

long bigNumber = 100L;
int smallNumber = (int)bigNumber;  // Explicit cast — OK if value fits

float f = (float)d;      // double → float — may lose precision

Be careful with overflow:

long huge = long.MaxValue;
int overflow = (int)huge;  // Silent overflow — result is -1!

// Safer — throws OverflowException if value doesn't fit:
int safe = checked((int)huge);

Whenever I cast between numeric types and the value might not fit, I wrap it in checked. Without it, C# silently overflows and you get a nonsense value with no exception, which is the kind of bug that survives code review and shows up in production a month later. On projects where the numbers matter, I turn on overflow checking globally with <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> in the .csproj so I cannot forget.

Fix 2: Use Conversion Methods for String ↔ Number

You cannot cast between strings and numbers. Use conversion methods:

String to number:

string input = "42";

// Parse — throws if invalid
int number = int.Parse(input);
double d = double.Parse(input);

// TryParse — returns false if invalid (preferred)
if (int.TryParse(input, out int result))
{
    Console.WriteLine(result);
}

// Convert class
int n = Convert.ToInt32(input);

Number to string:

int number = 42;
string s = number.ToString();
string formatted = number.ToString("N2");  // "42.00"
string interpolated = $"{number}";         // String interpolation

Common mistake, casting instead of converting:

string s = "42";
int n = (int)s;  // Error! Cannot cast string to int
int n = int.Parse(s);  // Correct

Fix 3: Handle Nullable Types

Nullable value types (int?, bool?, DateTime?) cannot be implicitly assigned to their non-nullable counterparts:

Broken:

int? nullable = GetValueOrNull();
int value = nullable;  // Error: Cannot convert int? to int

Fixed, null-coalescing operator:

int value = nullable ?? 0;  // Use 0 if null

Fixed, explicit check:

if (nullable.HasValue)
{
    int value = nullable.Value;
}

Fixed, .Value (throws if null):

int value = nullable.Value;  // InvalidOperationException if null

With nullable reference types (C# 8+):

string? name = GetName();
string nonNull = name;  // Warning: possible null reference

// Fixed:
string nonNull = name ?? "default";
string nonNull = name!;  // Null-forgiving operator (you assert it's not null)

The trap I have to actively resist is reaching for the null-forgiving operator (!) just to make the warning go away. All it does is move the crash from compile time, where it is free, to runtime, where it costs you a production incident. I only use ! when I can prove the value is non-null right there; otherwise I reach for ?? or an explicit check.

Fix 4: Fix Async/Await Return Types

Forgetting await gives you a Task<T> instead of T:

Broken:

async Task<string> GetDataAsync()
{
    return await httpClient.GetStringAsync("https://api.example.com/data");
}

// Calling without await:
string data = GetDataAsync();  // Error: Cannot convert Task<string> to string

Fixed, add await:

string data = await GetDataAsync();

Fixed, make the calling method async:

// Before:
public void ProcessData()
{
    string data = GetDataAsync();  // Error
}

// After:
public async Task ProcessData()
{
    string data = await GetDataAsync();  // Works
}

Common variations:

// Wrong return type:
async Task<int> Calculate()
{
    return "42";  // Error: Cannot convert string to int
}

// Missing async keyword:
Task<string> GetName()
{
    return "Alice";  // Error: Cannot convert string to Task<string>
}

// Fixed:
async Task<string> GetName()
{
    return "Alice";  // async methods wrap the return value in Task automatically
}
// Or without async:
Task<string> GetName()
{
    return Task.FromResult("Alice");
}

Fix 5: Fix LINQ Return Types

LINQ methods return specific types that might not match your variable:

Broken:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

// Where returns IEnumerable<int>, not List<int>
List<int> evens = numbers.Where(n => n % 2 == 0);  // Error!

// Select changes the element type
List<int> strings = numbers.Select(n => n.ToString());  // Error! IEnumerable<string>

// FirstOrDefault returns int?, not int (in .NET 6+)
int first = numbers.FirstOrDefault();  // May warn about nullable

Fixed, convert to the expected type:

List<int> evens = numbers.Where(n => n % 2 == 0).ToList();
List<string> strings = numbers.Select(n => n.ToString()).ToList();
int[] array = numbers.Where(n => n > 3).ToArray();

Fixed, use the correct variable type:

IEnumerable<int> evens = numbers.Where(n => n % 2 == 0);

Use var to let the compiler infer the type:

var evens = numbers.Where(n => n % 2 == 0);  // IEnumerable<int>
var first = numbers.FirstOrDefault();          // int

The deeper issue is that LINQ operators are lazy and return the interface, not the concrete collection. Where and Select return IEnumerable<T> because they describe a query that hasn’t run yet, not a materialized list. Assigning that to List<T> fails because a deferred query is not a list until you force it with .ToList() or .ToArray(). The rule of thumb: type the variable as IEnumerable<T> (or var) while you are still chaining operators, and only call .ToList() at the point where you actually need to iterate more than once, index into the result, or store it. Calling .ToList() prematurely in the middle of a chain runs the query early and discards LINQ’s main performance benefit.

Fix 6: Fix Generic Covariance Issues

Generic types are not automatically covariant in C#:

Broken:

class Animal { }
class Dog : Animal { }

List<Dog> dogs = new List<Dog>();
List<Animal> animals = dogs;  // Error! List<Dog> is not List<Animal>

Fixed, use interfaces with covariance:

List<Dog> dogs = new List<Dog>();
IEnumerable<Animal> animals = dogs;  // Works! IEnumerable<T> is covariant
IReadOnlyList<Animal> readOnly = dogs;  // Also works

Fixed, create a new list:

List<Animal> animals = dogs.Cast<Animal>().ToList();
// or
List<Animal> animals = new List<Animal>(dogs);
// or with LINQ
List<Animal> animals = dogs.Select(d => (Animal)d).ToList();

Why: List<T> is invariant because it has both Add (input) and indexer (output) operations. If List<Dog> could be assigned to List<Animal>, you could add a Cat to the list through the List<Animal> reference, corrupting the List<Dog>.

IEnumerable<T> is covariant (declared with out T) because it only produces values, never consumes them.

Fix 7: Fix Enum Conversions

Enums and integers require explicit conversion:

enum Color { Red = 0, Green = 1, Blue = 2 }

// Broken:
Color c = 1;       // Error: Cannot convert int to Color
int n = Color.Red; // Error: Cannot convert Color to int

// Fixed:
Color c = (Color)1;       // Explicit cast — Green
int n = (int)Color.Red;   // Explicit cast — 0

// String to enum:
Color c = Enum.Parse<Color>("Green");  // Color.Green
if (Enum.TryParse<Color>("Green", out Color result))
{
    Console.WriteLine(result);
}

Enum to string:

string name = Color.Red.ToString();  // "Red"
string name = nameof(Color.Red);     // "Red" (compile-time constant)

Fix 8: Fix Boolean Expression Issues

Common boolean conversion errors:

// Cannot convert int to bool (unlike C/C++):
int flag = 1;
if (flag) { }  // Error in C#!

// Fixed:
if (flag != 0) { }
if (flag == 1) { }

// Cannot convert string to bool:
string input = "true";
bool b = input;  // Error!

// Fixed:
bool b = bool.Parse(input);
bool b = input.Equals("true", StringComparison.OrdinalIgnoreCase);

Conditional expression type mismatch:

// Both branches must return the same type:
var result = condition ? "hello" : 42;  // Error: string vs int

// Fixed — make types match:
var result = condition ? "hello" : "42";
// or
object result = condition ? (object)"hello" : 42;

Conversions That Still Trip Me Up

Even after years of C#, these are the ones that still make me stop and stare for a minute. I once spent the better part of an afternoon stuck on the user-defined conversion trap below, so now I work through this list top to bottom whenever the error code does not match any of the cases above.

Check for user-defined implicit conversions. Custom types can define implicit conversion operators:

public class Celsius
{
    public double Temperature { get; }

    public static implicit operator Celsius(double temp) => new Celsius(temp);
    public static explicit operator double(Celsius c) => c.Temperature;
}

Celsius c = 100.0;          // Uses implicit operator
double d = (double)c;       // Uses explicit operator

Check for interface vs concrete type. You might need to cast to an interface:

IMyInterface obj = new MyClass();  // Works if MyClass implements IMyInterface
MyClass obj2 = obj;                // Error! Need explicit cast
MyClass obj2 = (MyClass)obj;       // Works (throws if wrong type)
MyClass obj2 = obj as MyClass;     // Returns null if wrong type

Check for dynamic type. If you need late-bound behavior:

dynamic value = GetUnknownType();
int n = value;  // Runtime conversion — may throw at runtime

Watch for var hiding the real type in error messages. When you write var x = SomeMethod(); and a later line fails to convert x, the compiler reports the inferred type, which may not be what you expected. Hover over the variable in your IDE, or temporarily replace var with the explicit type, to see what the compiler actually inferred. This is the fastest way to diagnose “Cannot convert” errors where the source type is surprising.

Target-typed new and conditional expressions (C# 9+). Newer C# infers types in both directions, which can produce conversion errors that older code didn’t have. int? x = condition ? 1 : null; compiles, but var x = condition ? 1 : null; fails because var cannot infer a common type for int and null. When a ternary reports a conversion error, give at least one branch an explicit type or annotate the target variable rather than relying on inference.

Method group vs delegate mismatch. Assigning a method name where a delegate of a different signature is expected produces a conversion error that mentions a method group: Cannot convert method group 'Foo' to 'Action<int>'. The method’s parameters or return type don’t match the delegate. Check the signatures line up exactly, including ref/out modifiers and return type.

For C# null reference exceptions, see Fix: C# NullReferenceException. For general type conversion in .NET, check the System.Convert class documentation.

Check your project’s nullable context. If you enabled <Nullable>enable</Nullable>, the compiler is stricter about nullable reference type assignments. Address warnings with proper null handling rather than suppressing them.

For TypeScript type errors that are similar in concept, see Fix: TypeScript cannot find name.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles