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

  1. 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.
  2. If you are unsure which dimension triggered it, the exception message names it in brackets — find that dimension by name and fix its Ratio.
  3. 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.
  4. 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

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


AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13). Data as JSON: /api/errors/7216e25f922aef39. Report an issue: GitHub.