{"record":{"id":"02031a97d4e82056","repo":"Stirling-Tools/Stirling-PDF","slug":"unsupported-unit-unit","errorCode":null,"errorMessage":"Unsupported unit: ${unit}","messagePattern":"Unsupported unit: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/core/utils/measurementUtils.ts","lineNumber":51,"sourceCode":"  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})`;\n}\n\n// Imperial units\nconst IMPERIAL_UNITS = [\"ft\", \"in\", \"yd\", \"mi\"] as const;\nexport function isImperialUnit(unit: string): boolean {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/utils/measurementUtils.ts#L33-L69","documentation":"Thrown by calculateScaleFactor after normalizeUnit(unit) (lowercase + trim) is not a key of POINT_TO_UNIT. Supported units are exactly m, cm, mm, km, ft, in, yd, mi. Any other string (including 'inch', 'meter', 'pixel', or whitespace variants not collapsed by trim) triggers it.","triggerScenarios":"Calling calculateScaleFactor(1, 'inch'); passing a localized full unit name ('centimeters'); a unit pulled from a persisted config that was saved with an unexpected token; trailing/internal whitespace normalizeUnit does not collapse (e.g. 'c m').","commonSituations":"i18n: storing display labels instead of canonical unit codes; migrating from an older unit list; third-party plugin reusing the helper with its own unit vocabulary.","solutions":["Always pass canonical unit codes (m, cm, mm, km, ft, in, yd, mi) — keep display labels separate from stored values.","Call getUnitFactor(unit) first; if it returns undefined, surface a friendly error before hitting calculateScaleFactor.","Add a unit picker UI constrained to the known MeasurementUnit type so free text never reaches the function.","Extend normalizeUnit to handle common aliases if you control the inputs."],"exampleFix":"// before\ncalculateScaleFactor(ratio, userTypedUnit);\n\n// after\nconst validUnits = [\"m\", \"cm\", \"mm\", \"km\", \"ft\", \"in\", \"yd\", \"mi\"] as const;\nif (!validUnits.includes(userTypedUnit.toLowerCase().trim() as never)) {\n  throw new Error(`Unknown unit \"${userTypedUnit}\". Use one of: ${validUnits.join(\", \")}`);\n}\ncalculateScaleFactor(ratio, userTypedUnit);","handlingStrategy":"validation","validationCode":"const SUPPORTED_UNITS = Object.keys(POINT_TO_UNIT);\nif (!SUPPORTED_UNITS.includes(unit.toLowerCase().trim())) {\n  throw new Error(`Unsupported unit \"${unit}\". Use: ${SUPPORTED_UNITS.join(\", \")}`);\n}\ncalculateScaleFactor(ratio, unit);","typeGuard":"function isMeasurementUnit(unit: string): unit is MeasurementUnit {\n  return Object.hasOwn(POINT_TO_UNIT, unit.toLowerCase().trim());\n}","tryCatchPattern":null,"preventionTips":["Store canonical unit codes (m, cm, mm, km, ft, in, yd, mi), never display labels, in config.","Bind the unit picker to the MeasurementUnit type so free text cannot reach the helper.","Pre-check getUnitFactor(unit) before calling calculateScaleFactor."],"tags":["validation","measurement","i18n","enums"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}