{"record":{"id":"066bad2bfab35227","repo":"microsoft/markitdown","slug":"malformed-data-uri-missing-separator","errorCode":null,"errorMessage":"Malformed data URI, missing ',' separator","messagePattern":"Malformed data URI, missing ',' separator","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"packages/markitdown/src/markitdown/_uri_utils.py","lineNumber":25,"sourceCode":"\ndef file_uri_to_path(file_uri: str) -> Tuple[str | None, str]:\n    \"\"\"Convert a file URI to a local file path\"\"\"\n    parsed = urlparse(file_uri)\n    if parsed.scheme != \"file\":\n        raise ValueError(f\"Not a file URL: {file_uri}\")\n\n    netloc = parsed.netloc if parsed.netloc else None\n    path = os.path.abspath(url2pathname(parsed.path))\n    return netloc, path\n\n\ndef parse_data_uri(uri: str) -> Tuple[str | None, Dict[str, str], bytes]:\n    if not uri.startswith(\"data:\"):\n        raise ValueError(\"Not a data URI\")\n\n    header, _, data = uri.partition(\",\")\n    if not _:\n        raise ValueError(\"Malformed data URI, missing ',' separator\")\n\n    meta = header[5:]  # Strip 'data:'\n    parts = meta.split(\";\")\n\n    is_base64 = False\n    # Ends with base64?\n    if parts[-1] == \"base64\":\n        parts.pop()\n        is_base64 = True\n\n    mime_type = None  # Normally this would default to text/plain but we won't assume\n    if len(parts) and len(parts[0]) > 0:\n        # First part is the mime type\n        mime_type = parts.pop(0)\n\n    attributes: Dict[str, str] = {}\n    for part in parts:\n        # Handle key=value pairs in the middle","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/microsoft/markitdown/blob/fd239d5d2be43d9b68329730206b9312c7d5a388/packages/markitdown/src/markitdown/_uri_utils.py#L7-L43","documentation":"RFC 2397 data URIs are '<mediatype>[;base64],<data>'; parse_data_uri() splits on the first comma and treats an empty separator (no comma at all) as malformed, raising this ValueError. The check uses uri.partition(',') so a URI that ends at the header ('data:text/plain') with no ',' and no payload triggers it.","triggerScenarios":"md.convert_uri('data:text/plain') or 'data:application/pdf' — header present but the comma and data section missing; also truncated copies of data URIs.","commonSituations":"Template strings or string concatenation that drops the payload, clipboard truncation of very long base64 URIs, or building data URIs with a missing ',' separator during generation.","solutions":["Fix the generator to always emit '<mime>[;base64],<data>' with the comma","Validate user-supplied data URIs with a regex before conversion","If the payload is empty on purpose, send 'data:text/plain,'"],"exampleFix":"# before\nmd.convert_uri(\"data:application/pdf\")  # ValueError: missing ','\n\n# after\nmd.convert_uri(\"data:application/pdf;base64,JVBERi0...\")","handlingStrategy":"validation","validationCode":"import re\n\nDATA_URI_RE = re.compile(r\"^data:[^,]*;base64,[A-Za-z0-9+/=]+$|^data:[^,]*,.+$\")\n\ndef is_wellformed_data_uri(uri: str) -> bool:\n    return uri.startswith(\"data:\") and \",\" in uri","typeGuard":null,"tryCatchPattern":"try:\n    result = md.convert_uri(data_uri)\nexcept ValueError as e:\n    if \"missing ','\" in str(e):\n        raise ValueError(\"data URI truncated: header present but no payload\") from e\n    raise","preventionTips":["Validate data URIs with a regex before storing or converting them","When concatenating base64 payloads, always template as 'data:<mime>;base64,<payload>'","Guard against silent truncation of very long URIs in logs, clipboards, and config files"],"tags":["uri","data-uri","malformed-input"],"backgroundTag":null,"analyzedSha":"fd239d5d2be43d9b68329730206b9312c7d5a388","analyzedAt":"2026-08-14T15:47:51.745Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}