nopSolutions/nopCommerce · error · NopException
Exchange ratio not set for weight [{targetMeasureWeight.Name
Error message
Exchange ratio not set for weight [{targetMeasureWeight.Name}] What it means
Thrown by MeasureService.ConvertFromPrimaryMeasureWeightAsync when converting a value FROM the base weight TO a target MeasureWeight whose Ratio is 0 (decimal.Zero). nopCommerce multiplies by Ratio, so a zero target ratio is treated as unconfigured and aborts with NopException instead of silently yielding zero for every conversion.
Source
Thrown at src/Libraries/Nop.Services/Directory/MeasureService.cs:349
/// <param name="value">Value to convert</param>
/// <param name="targetMeasureWeight">Target weight</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the converted value
/// </returns>
public virtual async Task<decimal> ConvertFromPrimaryMeasureWeightAsync(decimal value,
MeasureWeight targetMeasureWeight)
{
ArgumentNullException.ThrowIfNull(targetMeasureWeight);
var result = value;
var baseWeightIn = await GetMeasureWeightByIdAsync(_measureSettings.BaseWeightId);
if (result == decimal.Zero || targetMeasureWeight.Id == baseWeightIn.Id)
return result;
var exchangeRatio = targetMeasureWeight.Ratio;
if (exchangeRatio == decimal.Zero)
throw new NopException($"Exchange ratio not set for weight [{targetMeasureWeight.Name}]");
result *= exchangeRatio;
return result;
}
/// <summary>
/// Converts to primary measure weight
/// </summary>
/// <param name="value">Value to convert</param>
/// <param name="sourceMeasureWeight">Source weight</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the converted value
/// </returns>
public virtual async Task<decimal> ConvertToPrimaryMeasureWeightAsync(decimal value, MeasureWeight sourceMeasureWeight)
{
ArgumentNullException.ThrowIfNull(sourceMeasureWeight);
View on GitHub (pinned to 64bdf2ff08)
Solutions
- In Configuration > Measures > Weights, open the target weight unit named in the error and set a non-zero Ratio relative to the base weight.
- If the shipping plugin (UPS) triggers it, verify the plugin's selected MeasureWeight still exists and has a ratio after any DB restore or migration.
- Guard the caller: skip conversion (or use the base value) when targetMeasureWeight.Ratio == decimal.Zero.
- Ensure BaseWeightId in measure settings still points to an existing, configured weight.
Example fix
// before
weight = await _measureService.ConvertFromPrimaryMeasureWeightAsync(weight, _measureWeight);
// after — guard the target weight ratio before the call
if (weight != decimal.Zero && _measureWeight.Id != baseWeightId && _measureWeight.Ratio == decimal.Zero)
throw new InvalidOperationException($"Weight '{_measureWeight.Name}' has no ratio; configure it in Configuration > Measures.");
weight = await _measureService.ConvertFromPrimaryMeasureWeightAsync(weight, _measureWeight); Defensive patterns
Strategy: validation
Validate before calling
// Validate before converting from the base weight to a target weight
var baseWt = await _measureService.GetMeasureWeightByIdAsync(_measureSettings.BaseWeightId);
if (value != decimal.Zero && targetMeasureWeight.Id != baseWt.Id && targetMeasureWeight.Ratio == decimal.Zero)
throw new InvalidOperationException($"Configure a non-zero Ratio for weight '{targetMeasureWeight.Name}'.");
var converted = await _measureService.ConvertFromPrimaryMeasureWeightAsync(value, targetMeasureWeight); Type guard
static bool IsTargetWeightConvertible(MeasureWeight target, MeasureWeight baseWt, decimal value)
=> value == decimal.Zero || target.Id == baseWt.Id || target.Ratio != decimal.Zero; Try / catch
try { weight = await _measureService.ConvertFromPrimaryMeasureWeightAsync(weight, targetWt); }
catch (NopException ex) when (ex.Message.StartsWith("Exchange ratio not set for weight"))
{ /* report 'configure the target weight ratio'; do not silently return value */ } Prevention
- When configuring a shipping plugin that converts weights, verify its MeasureWeight has a non-zero Ratio.
- Keep a startup health check that asserts all published weights have Ratio != 0.
- After restoring a DB, re-verify weight ratios before enabling shipping.
When it happens
Trigger: Calling ConvertFromPrimaryMeasureWeightAsync(value, targetMeasureWeight) where targetMeasureWeight.Ratio == 0m, targetMeasureWeight.Id != BaseWeightId, and value != 0m. Hit indirectly through ConvertMeasureWeightAsync or directly by plugins (e.g. UPS shipping at UPSService.cs:774, Omnisend at OmnisendEventsService.cs:392).
Common situations: A shipping plugin (UPS) converts package weight into its configured MeasureWeight but that weight's Ratio is 0; a newly added weight unit lacks a ratio; the base weight was changed but other weight units were not re-normalized; sample data installed without weight ratios.
Related errors
- Exchange ratio not set for weight [{sourceMeasureWeight.Name
- Exchange ratio not set for dimension [{sourceMeasureDimensio
- Shipping total couldn't be calculated
- No available shipping options
- Client ID is not set
AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13).
Data as JSON: /api/errors/b9b81e311d7ca2fe.
Report an issue: GitHub.