{"record":{"id":"460264f381fc83d3","repo":"MadsLorentzen/ai-job-search","slug":"ambiguous-comma-separator","errorCode":null,"errorMessage":"ambiguous comma separator","messagePattern":"ambiguous comma separator","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/convert_salary_excel.py","lineNumber":77,"sourceCode":"    if isinstance(value, (int, float)):\n        return float(value)\n    if not isinstance(value, str):\n        raise ValueError(\"not numeric\")\n\n    text = value.strip().replace(\"\\u00a0\", \" \").replace(\" \", \"\")\n    if not text:\n        raise ValueError(\"not numeric\")\n    if \",\" in text and \".\" in text:\n        # The separator that appears last is the decimal separator: European\n        # \"1.234,56\" and US \"1,234.56\" are both unambiguous here, unlike the\n        # single-separator cases below.\n        if text.rfind(\",\") > text.rfind(\".\"):\n            text = text.replace(\".\", \"\").replace(\",\", \".\")\n        else:\n            text = text.replace(\",\", \"\")\n    elif \",\" in text:\n        if re.fullmatch(r\"[+-]?\\d+,\\d{3}\", text):\n            raise ValueError(\"ambiguous comma separator\")\n        text = text.replace(\",\", \".\")\n    elif \".\" in text:\n        if re.fullmatch(r\"[+-]?\\d+\\.\\d{3}\", text):\n            raise ValueError(\"ambiguous dot separator\")\n    return float(text)\n\n\ndef header_matches(header, patterns):\n    \"\"\"Return True when a header contains a meaningful pattern match.\n\n    Patterns match whole tokens; any pattern also listed in\n    ``COMPOUND_PATTERNS`` may additionally match as a substring, to handle\n    languages that form compound words.\n    \"\"\"\n    h = header.lower().strip()\n    tokens = set(re.findall(r\"[a-zæøåöäü0-9]+\", h))\n\n    for p in patterns:","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/MadsLorentzen/ai-job-search/blob/79cd383e58f0af7948c7c6462a3a289e9b67421e/tools/convert_salary_excel.py#L59-L95","documentation":"Raised when a string contains exactly one comma in the pattern digits,3-digits (e.g. '1,234'). This matches both US thousands ('1,234' = 1234) and European decimal ('1,234' = 1.234), so converting it either way risks a 1000x error; the function refuses rather than guess.","triggerScenarios":"Passing a string like '1,234', '42,000', or '-9,999' that fullmatches r'[+-]?\\d+,\\d{3}'. Common when a sheet has US-formatted thousands without a decimal part, or a European decimal with exactly three fraction digits.","commonSituations":"Excel cells formatted with thousands separators exported as text ('1,234'), or a locale mismatch: a Danish/European sheet where '1,234' means 1.234 parsed with US assumptions. Any single-group thousands value with exactly 3 digits after the comma triggers it by design.","solutions":["Disambiguate the locale before parsing: if you know the sheet is European, rewrite the comma to a dot first ('1,234' -> '1.234'); if US, strip the comma","Add more context, e.g. pass values with an explicit decimal part ('1,234.0' or '1,234,00' are unambiguous)","Catch this ValueError in parse_sheet, log the raw cell, and resolve manually or via a locale parameter"],"exampleFix":"// before\nnum = parse_numeric_cell('1,234')  # raises\n\n// after\nnum = parse_numeric_cell('1,234'.replace(',', '') if locale == 'us' else '1,234'.replace(',', '.'))","handlingStrategy":"try-catch","validationCode":"import re\nAMBIG = re.compile(r'[+-]?\\d+,\\d{3}$')\nif isinstance(v, str) and AMBIG.fullmatch(v.strip()):\n    v = v.replace(',', '.' if locale == 'eu' else '')","typeGuard":"def is_ambiguous_comma(v) -> bool:\n    return isinstance(v, str) and bool(re.fullmatch(r'[+-]?\\d+,\\d{3}', v.strip()))","tryCatchPattern":"try:\n    num = parse_numeric_cell(v)\nexcept ValueError as e:\n    if 'ambiguous comma' in str(e):\n        raise LocaleNeeded(v)  # surface for manual/locale resolution\n    raise","preventionTips":["Know the sheet's locale before parsing and pre-normalize separators","Export source data with explicit decimals so formats are unambiguous","Catch the ambiguity error and resolve per-column with a locale flag"],"tags":["python","excel","locale","number-formatting","ambiguity"],"backgroundTag":"locale-number-parsing","analyzedSha":"79cd383e58f0af7948c7c6462a3a289e9b67421e","analyzedAt":"2026-08-27T21:51:12.330Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}