{"record":{"id":"7216e25f922aef39","repo":"nopSolutions/nopCommerce","slug":"exchange-ratio-not-set-for-dimension-sourcemeasu","errorCode":null,"errorMessage":"Exchange ratio not set for dimension [{sourceMeasureDimension.Name}]","messagePattern":"Exchange ratio not set for dimension \\[(.+?)\\]","errorType":"exception","errorClass":"NopException","httpStatus":null,"severity":"error","filePath":"src/Libraries/Nop.Services/Directory/MeasureService.cs","lineNumber":201,"sourceCode":"    /// <param name=\"value\">Value to convert</param>\r\n    /// <param name=\"sourceMeasureDimension\">Source dimension</param>\r\n    /// <returns>\r\n    /// A task that represents the asynchronous operation\r\n    /// The task result contains the converted value\r\n    /// </returns>\r\n    public virtual async Task<decimal> ConvertToPrimaryMeasureDimensionAsync(decimal value,\r\n        MeasureDimension sourceMeasureDimension)\r\n    {\r\n        ArgumentNullException.ThrowIfNull(sourceMeasureDimension);\r\n\r\n        var result = value;\r\n        var baseDimensionIn = await GetMeasureDimensionByIdAsync(_measureSettings.BaseDimensionId);\r\n        if (result == decimal.Zero || sourceMeasureDimension.Id == baseDimensionIn.Id)\r\n            return result;\r\n\r\n        var exchangeRatio = sourceMeasureDimension.Ratio;\r\n        if (exchangeRatio == decimal.Zero)\r\n            throw new NopException($\"Exchange ratio not set for dimension [{sourceMeasureDimension.Name}]\");\r\n        result /= exchangeRatio;\r\n\r\n        return result;\r\n    }\r\n\r\n\r\n    #endregion\r\n\r\n    #region Weights\r\n\r\n    /// <summary>\r\n    /// Deletes measure weight\r\n    /// </summary>\r\n    /// <param name=\"measureWeight\">Measure weight</param>\r\n    /// <returns>A task that represents the asynchronous operation</returns>\r\n    public virtual async Task DeleteMeasureWeightAsync(MeasureWeight measureWeight)\r\n    {\r\n        await _measureWeightRepository.DeleteAsync(measureWeight);\r","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/nopSolutions/nopCommerce/blob/64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2/src/Libraries/Nop.Services/Directory/MeasureService.cs#L183-L219","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar meters = await _measureService.ConvertToPrimaryMeasureDimensionAsync(length, dimension);\n\n// after — guard the ratio before converting\nif (dimension.Id != baseDim.Id && length != decimal.Zero && dimension.Ratio == decimal.Zero)\n    throw new InvalidOperationException($\"Dimension '{dimension.Name}' has no ratio; configure it in Configuration > Measures.\");\nvar meters = await _measureService.ConvertToPrimaryMeasureDimensionAsync(length, dimension);","handlingStrategy":"validation","validationCode":"// Validate before converting a dimension to the base dimension\nvar baseDim = await _measureService.GetMeasureDimensionByIdAsync(_measureSettings.BaseDimensionId);\nif (sourceMeasureDimension.Id != baseDim.Id && value != decimal.Zero && sourceMeasureDimension.Ratio == decimal.Zero)\n    throw new InvalidOperationException($\"Configure a non-zero Ratio for dimension '{sourceMeasureDimension.Name}'.\");\nvar converted = await _measureService.ConvertToPrimaryMeasureDimensionAsync(value, sourceMeasureDimension);","typeGuard":"// Ensure the dimension is conversion-ready before passing it in\nstatic bool IsDimensionConvertible(MeasureDimension dim, MeasureDimension baseDim, decimal value)\n    => value == decimal.Zero || dim.Id == baseDim.Id || dim.Ratio != decimal.Zero;","tryCatchPattern":"try { result = await _measureService.ConvertToPrimaryMeasureDimensionAsync(value, dim); }\ncatch (NopException ex) when (ex.Message.StartsWith(\"Exchange ratio not set for dimension\"))\n{ /* surface 'configure the dimension ratio' to the admin; do not fall back to a silent value */ }","preventionTips":["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."],"tags":["measure","configuration","nopcommerce","conversion"],"backgroundTag":null,"analyzedSha":"64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2","analyzedAt":"2026-08-13T21:19:38.062Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}