nopSolutions/nopCommerce · error · NopException
Exchange ratio not set for dimension [{sourceMeasureDimensio
Error message
Exchange ratio not set for dimension [{sourceMeasureDimension.Name}] What it means
Thrown by MeasureService.ConvertToPrimaryMeasureDimensionAsync when converting a quantity from a source MeasureDimension to the configured base dimension, but the source dimension's Ratio property is 0 (decimal.Zero). nopCommerce divides the value by Ratio, so a zero ratio is treated as an unconfigured dimension and aborts with NopException rather than producing a wrong (infinite) result.
Source
Thrown at src/Libraries/Nop.Services/Directory/MeasureService.cs:201
/// <param name="value">Value to convert</param>
/// <param name="sourceMeasureDimension">Source dimension</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the converted value
/// </returns>
public virtual async Task<decimal> ConvertToPrimaryMeasureDimensionAsync(decimal value,
MeasureDimension sourceMeasureDimension)
{
ArgumentNullException.ThrowIfNull(sourceMeasureDimension);
var result = value;
var baseDimensionIn = await GetMeasureDimensionByIdAsync(_measureSettings.BaseDimensionId);
if (result == decimal.Zero || sourceMeasureDimension.Id == baseDimensionIn.Id)
return result;
var exchangeRatio = sourceMeasureDimension.Ratio;
if (exchangeRatio == decimal.Zero)
throw new NopException($"Exchange ratio not set for dimension [{sourceMeasureDimension.Name}]");
result /= exchangeRatio;
return result;
}
#endregion
#region Weights
/// <summary>
/// Deletes measure weight
/// </summary>
/// <param name="measureWeight">Measure weight</param>
/// <returns>A task that represents the asynchronous operation</returns>
public virtual async Task DeleteMeasureWeightAsync(MeasureWeight measureWeight)
{
await _measureWeightRepository.DeleteAsync(measureWeight);
View on GitHub (pinned to 64bdf2ff08)
Solutions
- In the admin area (Configuration > Measures > Dimensions), open the offending dimension and set a non-zero Ratio relative to the base dimension, then the conversion succeeds.
- If you are unsure which dimension triggered it, the exception message names it in brackets — find that dimension by name and fix its Ratio.
- If the value genuinely represents the base dimension already, pass the base dimension object (or zero value) instead so the early-return guard at line 196 short-circuits.
- Programmatically, guard the caller: only call the converter when sourceMeasureDimension.Ratio != decimal.Zero.
Example fix
// before
var meters = await _measureService.ConvertToPrimaryMeasureDimensionAsync(length, dimension);
// after — guard the ratio before converting
if (dimension.Id != baseDim.Id && length != decimal.Zero && dimension.Ratio == decimal.Zero)
throw new InvalidOperationException($"Dimension '{dimension.Name}' has no ratio; configure it in Configuration > Measures.");
var meters = await _measureService.ConvertToPrimaryMeasureDimensionAsync(length, dimension); Defensive patterns
Strategy: validation
Validate before calling
// Validate before converting a dimension to the base dimension
var baseDim = await _measureService.GetMeasureDimensionByIdAsync(_measureSettings.BaseDimensionId);
if (sourceMeasureDimension.Id != baseDim.Id && value != decimal.Zero && sourceMeasureDimension.Ratio == decimal.Zero)
throw new InvalidOperationException($"Configure a non-zero Ratio for dimension '{sourceMeasureDimension.Name}'.");
var converted = await _measureService.ConvertToPrimaryMeasureDimensionAsync(value, sourceMeasureDimension); Type guard
// Ensure the dimension is conversion-ready before passing it in
static bool IsDimensionConvertible(MeasureDimension dim, MeasureDimension baseDim, decimal value)
=> value == decimal.Zero || dim.Id == baseDim.Id || dim.Ratio != decimal.Zero; Try / catch
try { result = await _measureService.ConvertToPrimaryMeasureDimensionAsync(value, dim); }
catch (NopException ex) when (ex.Message.StartsWith("Exchange ratio not set for dimension"))
{ /* surface 'configure the dimension ratio' to the admin; do not fall back to a silent value */ } Prevention
- Never insert MeasureDimension rows with a default Ratio of 0; require a ratio in your seeding/migration scripts.
- After changing BaseDimensionId, audit every dimension's Ratio so it is relative to the new base.
- Add a DB check constraint or integration test asserting published dimensions have Ratio != 0.
When it happens
Trigger: Calling ConvertToPrimaryMeasureDimensionAsync(value, sourceMeasureDimension) where sourceMeasureDimension.Ratio == 0m, sourceMeasureDimension.Id != BaseDimensionId, and value != 0m. Reached indirectly via ConvertMeasureDimensionAsync (the public dimension-conversion entry point) or by any plugin that calls the primary-dimension converter directly.
Common situations: An admin adds a new length/measure dimension in Configuration > Measures but forgets to fill the Ratio field; a fresh install or migrated DB leaves Ratio at its default 0; a dimension seeded by sample data has its ratio cleared; a custom integration imports MeasureDimension rows without ratios.
Related errors
- Exchange ratio not set for weight [{targetMeasureWeight.Name
- Exchange ratio not set for weight [{sourceMeasureWeight.Name
- Data provider supports connection only with login and passwo
- Data provider supports connection only with login and passwo
- {httpResponse.ReasonPhrase}
AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13).
Data as JSON: /api/errors/7216e25f922aef39.
Report an issue: GitHub.