Quick Answer
CS0161: 'FixedBot.VolumeFromRisk(double)': not all code paths return a value means a method that promises to return a value (a double, a bool, an int) has at least one way to reach its closing bracket without a return. The compiler checks every possible path through the method. If one path ends without returning something, it refuses to build.
In a cBot this nearly always comes from a helper method with an if and no else: the code returns a value when the condition is true and falls off the end when it is false. The fix is to return a value on every path: add a final return after the if, add an else, or restructure the method so that the failing case returns first and the normal case returns at the end.
From the author: I build cBots in cTrader Automate and convert strategies from Pine Script and MQL5 to C#. CS0161 is typical after a hand conversion, because a Pine function always returns its last expression, and an MQL5 function with a missing return is only a warning in some cases. C# is stricter, and this guide shows the cases and the safest fix for each.
Key Facts
| What you see | Usual cause | Fix |
|---|---|---|
| if (x) { return a; } and nothing after it | No return when x is false | Add return b; after the if |
| if ... else if ... without a final else | Not every case is covered | Add a final else or a final return |
| switch without default that returns | A value outside the cases | Add a default: that returns |
| return only inside a foreach or for | The loop can run zero times | Return a value after the loop |
| A try block that returns, a catch that does not | The error path has no return | Return in the catch or after it |
| A method that should be void | A return type was written by mistake | Change it to void, or add the return |
What the Compiler Is Telling You
CS0161: 'BrokenBot.VolumeFromRisk(double)': not all code paths return a value
- The name in quotes is the method that is missing a return, with its class and parameter types.
- The line points to the method, not to the place where the return is missing. You have to read the method and find the path that ends without a return.
The Broken cBot
This cBot calculates a volume from the risk and prints it once per bar. It does not place trades. Two helper methods have a path without a return:
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class BrokenBot : Robot
{
[Parameter("Risk %", DefaultValue = 1.0, MinValue = 0.1, MaxValue = 5.0)]
public double RiskPercent { get; set; }
[Parameter("Stop Loss (pips)", DefaultValue = 20, MinValue = 1)]
public double StopLossPips { get; set; }
protected override void OnBar()
{
double volume = VolumeFromRisk(StopLossPips);
bool hasPosition = HasOpenPosition();
Print("Volume {0}, has position: {1}", volume, hasPosition);
}
private double VolumeFromRisk(double stopLossPips) // CS0161
{
if (stopLossPips > 0)
{
double riskMoney = Account.Balance * RiskPercent / 100.0;
double units = riskMoney / (stopLossPips * Symbol.PipValue);
return Symbol.NormalizeVolumeInUnits(units, RoundingMode.Down);
}
}
private bool HasOpenPosition() // CS0161
{
foreach (var position in Positions)
{
if (position.SymbolName == SymbolName)
return true;
}
}
}
}
In VolumeFromRisk, nothing is returned when stopLossPips is 0 or negative. In HasOpenPosition, nothing is returned when the loop finds no position, or when there are no positions at all.
Cause 1: An if Without an else
The most common case. Both branches must return, or a return must follow the if:
private double VolumeFromRisk(double stopLossPips)
{
if (stopLossPips <= 0)
return 0; // the failing case returns first
double riskMoney = Account.Balance * RiskPercent / 100.0;
double units = riskMoney / (stopLossPips * Symbol.PipValue);
return Symbol.NormalizeVolumeInUnits(units, RoundingMode.Down);
}
Returning early for the bad case keeps the normal path flat and at the end of the method, and it also protects against a division by zero. The sizing method is explained in Pine Script strategy.entry to cTrader C#. Decide what the "bad" return value means for the caller: a volume of 0 should make the caller skip the trade, so check it before you send an order.
Cause 2: A Loop That May Not Find Anything
A return inside a loop does not cover the case where the loop ends without it, and a loop over an empty collection never runs its body:
private bool HasOpenPosition()
{
foreach (var position in Positions)
{
if (position.SymbolName == SymbolName)
return true;
}
return false; // no match, or no positions at all
}
The same method can be written with LINQ, which also removes the problem:
using System.Linq;
private bool HasOpenPosition()
{
return Positions.Any(p => p.SymbolName == SymbolName);
}
This needs using System.Linq;, and without it the error is CS1061, explained in cTrader C# error CS1061.
Cause 3: if, else if and No Final else
If the conditions cover the cases you thought of, the compiler still sees the case that none of them matches:
private string Describe(double close, double ema)
{
if (close > ema)
return "above";
else if (close < ema)
return "below";
// close == ema is not covered: CS0161
}
Add the missing case as a final else, which also documents the third state:
private string Describe(double close, double ema)
{
if (close > ema)
return "above";
else if (close < ema)
return "below";
else
return "equal";
}
Cause 4: A switch Without a default
A switch over an int or an enum can receive a value that you did not list. For an enum, the compiler still does not assume that you listed all the values. Add a default:
private double Direction(TradeType type)
{
switch (type)
{
case TradeType.Buy:
return 1.0;
case TradeType.Sell:
return -1.0;
default:
return 0.0;
}
}
Cause 5: A try Without a Return in the catch
If the try block returns a value and the catch block only prints a message, the path through the catch has no return:
private double SafeRatio(double a, double b)
{
try
{
return a / b;
}
catch (Exception ex)
{
Print("Error: {0}", ex.Message);
return 0; // needed: this path must return too
}
}
This needs using System; for Exception. Dividing a double by zero does not throw in C# (it gives infinity or NaN), so a check before the division is a better guard than a try.
Cause 6: The Method Should Be void
If a method does not give a value back, its type is void, and it needs no return value;. A return type that was copied from another method, or left over after an edit, causes the same error:
private void LogState() // void: nothing to return
{
Print("Balance {0}", Account.Balance);
}
Call the method as a statement (LogState();), not as part of an expression.
The Fixed cBot
using System.Linq;
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class FixedBot : Robot
{
[Parameter("Risk %", DefaultValue = 1.0, MinValue = 0.1, MaxValue = 5.0)]
public double RiskPercent { get; set; }
[Parameter("Stop Loss (pips)", DefaultValue = 20, MinValue = 1)]
public double StopLossPips { get; set; }
protected override void OnBar()
{
double volume = VolumeFromRisk(StopLossPips);
bool hasPosition = HasOpenPosition();
Print("Volume {0}, has position: {1}", volume, hasPosition);
}
private double VolumeFromRisk(double stopLossPips)
{
if (stopLossPips <= 0)
return 0;
double riskMoney = Account.Balance * RiskPercent / 100.0;
double units = riskMoney / (stopLossPips * Symbol.PipValue);
return Symbol.NormalizeVolumeInUnits(units, RoundingMode.Down);
}
private bool HasOpenPosition()
{
return Positions.Any(p => p.SymbolName == SymbolName);
}
}
}
The bot only prints, so you can check the volume against your own calculation before it trades.
CS0161 and Similar Errors
| Error | Message | What it means |
|---|---|---|
| CS0161 | Not all code paths return a value | A path through the method ends without a return |
| CS0127 | Since 'M' returns void, a return keyword must not be followed by an object expression | A void method returns a value |
| CS0165 | Use of unassigned local variable 'x' | A variable is read before it has a value |
| CS0266 | Cannot implicitly convert type 'double' to 'int' | A return or assignment needs a cast |
| CS1061 | 'X' does not contain a definition for 'Y' | The member after the dot does not exist |
Checklist: What to Check When You See CS0161
- Read the method name in the message and open the method.
- Follow each path from the top to the closing bracket and check that each ends with a
return. - Look at every
ifwithout anelse, everyswitchwithout adefaultand every loop. - Add the missing return, or restructure with an early return for the failing case.
- Decide what the failing case returns (0, false, null) and handle it in the caller.
- Should it be
void? If nothing is returned, change the type. - Build again and print the result to check it.
- Test on a demo account before the cBot trades with real money.
Common Mistakes
- Returning only inside an
if. - Returning only inside a loop.
- No final
elseafterelse ifbranches. - A
switchwithoutdefault. - Returning 0 for a bad case and then trading with it. Check the value in the caller.
- Leaving a wrong return type after an edit.
FAQ
Related Guides
- cTrader C# error CS1061: the object is found, the member is not.
- cTrader C# error CS1503: an argument of the wrong type in a call.
- cTrader C# error CS0246: a type name the compiler does not know.
- cTrader C# error CS0103: the name does not exist in the current context.
- Pine Script strategy.entry to cTrader C#: risk-based volume and position checks.
- MQL5 zero divide error: guard a division before you use it.
- What to check in a trading bot before going live: a pre-live checklist for stops, lot size, spread and trade results.
- cTrader C# error CS0136: the same name used twice in a method.
Fix Compile Errors Without Guessing
If a cBot or an Expert Advisor does not build, paste the code and the error message into the CodeFlowOS Error Fixer. It suggests a minimal fix, explains each error and shows what changed. A fix uses 1 credit, with up to 3 attempts included, and the compiler check is optional. Outputs are not guaranteed, and code that compiles can still have wrong trading logic, so always review the result. Before a bot goes live, the Code Review is free during the early-bird period. CodeFlowOS also converts strategies between Pine Script, MQL5 and cTrader C#: try the converter page.
This article is educational. It does not describe trading performance and is not financial advice. Always build the code in cTrader Automate and backtest it before using it on a live account.