{"record":{"id":"003081773fb3d185","repo":"Stirling-Tools/Stirling-PDF","slug":"invalid-scale-ratio-ratio","errorCode":null,"errorMessage":"Invalid scale ratio: ${ratio}","messagePattern":"Invalid scale ratio: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/core/utils/measurementUtils.ts","lineNumber":46,"sourceCode":"function normalizeUnit(unit: string): string {\n  return unit.toLowerCase().trim();\n}\n\nfunction isMeasurementUnit(unit: string): unit is MeasurementUnit {\n  return Object.hasOwn(POINT_TO_UNIT, unit);\n}\n\nexport function getUnitFactor(unit: string): number | undefined {\n  const normalized = normalizeUnit(unit);\n  if (!isMeasurementUnit(normalized)) {\n    return undefined;\n  }\n  return POINT_TO_UNIT[normalized];\n}\n\nexport function calculateScaleFactor(ratio: number, unit: string): number {\n  if (!Number.isFinite(ratio) || ratio <= 0) {\n    throw new Error(`Invalid scale ratio: ${ratio}`);\n  }\n\n  const normalized = normalizeUnit(unit);\n  if (!isMeasurementUnit(normalized)) {\n    throw new Error(`Unsupported unit: ${unit}`);\n  }\n\n  return POINT_TO_UNIT[normalized] * ratio;\n}\n\nexport function generateScaleLabel(ratio: number | null, unit: string): string {\n  if (ratio === null || ratio === undefined) {\n    return unit;\n  }\n  const display = Number.isInteger(ratio)\n    ? ratio.toString()\n    : ratio.toFixed(2).replace(/\\.?0+$/, \"\");\n  return `1:${display} (${unit})`;","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/utils/measurementUtils.ts#L28-L64","documentation":"Thrown by calculateScaleFactor when the passed ratio fails Number.isFinite(ratio) || ratio <= 0. ratio is multiplied against a unit factor to produce a scale, so NaN, Infinity, or non-positive values are rejected as mathematically invalid (they would yield a nonsensical measurement scale).","triggerScenarios":"Calling calculateScaleFactor(NaN, 'cm'); passing a ratio parsed from a malformed preset string (e.g. '1:abc'); dividing by zero upstream and forwarding the result; a measurement tool emitting 0 or negative from a degenerate two-click calibration.","commonSituations":"Two calibration clicks on the exact same point (distance 0); user typing non-numeric text into a ratio field; float underflow producing 0 from extremely close points.","solutions":["Validate and coerce the ratio before calling: ensure it is a finite number > 0.","If the ratio originates from user input, parse with parsePresetRatio and reject null before calling calculateScaleFactor.","Guard calibration handlers against coincident points (distance === 0) and prompt the user to drag further.","Default to a sane ratio (e.g. 1) with a warning instead of forwarding an invalid value."],"exampleFix":"// before\nconst factor = calculateScaleFactor(ratio, unit);\n\n// after\nif (!Number.isFinite(ratio) || ratio <= 0) {\n  throw new Error(`Cannot calibrate: scale ratio is ${ratio}. Pick two distinct points.`);\n}\nconst factor = calculateScaleFactor(ratio, unit);","handlingStrategy":"validation","validationCode":"function assertValidRatio(ratio: number): void {\n  if (!Number.isFinite(ratio) || ratio <= 0) {\n    throw new Error(`Cannot use scale ratio ${ratio}: must be a positive finite number.`);\n  }\n}\n\nassertValidRatio(ratio);\nconst factor = calculateScaleFactor(ratio, unit);","typeGuard":"const isValidRatio = (ratio: unknown): ratio is number =>\n  typeof ratio === \"number\" && Number.isFinite(ratio) && ratio > 0;","tryCatchPattern":null,"preventionTips":["Validate ratio at the gesture handler (reject coincident calibration points).","Parse user-entered ratios with parsePresetRatio and reject null.","Never forward division results to calculateScaleFactor without an isFinite check."],"tags":["validation","measurement","math","user-input"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}