{"record":{"id":"d063afd8436c5d18","repo":"ComposioHQ/composio","slug":"refusing-to-write-label-containing-invalid-unico","errorCode":null,"errorMessage":"Refusing to write {label} containing invalid Unicode: {name!r}","messagePattern":"Refusing to write (.+?) containing invalid Unicode: (.+?)","errorType":"exception","errorClass":"UnsafePathComponentError","httpStatus":null,"severity":"error","filePath":"python/composio/utils/safe_path.py","lineNumber":194,"sourceCode":"    if \"\\x00\" in raw_basename:\n        raise UnsafePathComponentError(\n            f\"Refusing to write {label} containing a NUL byte: {name!r}\"\n        )\n    if any(ord(char) < 32 or char in '<>:\"|?*' for char in raw_basename):\n        raise UnsafePathComponentError(\n            f\"Refusing to write {label} containing characters reserved by \"\n            f\"Windows: {name!r}\"\n        )\n    if raw_basename.endswith((\" \", \".\")):\n        raise UnsafePathComponentError(\n            f\"Refusing to write {label} ending in a space or dot: {name!r}\"\n        )\n\n    basename = raw_basename.strip()\n    try:\n        encoded_length = len(os.fsencode(basename))\n    except UnicodeEncodeError as e:\n        raise UnsafePathComponentError(\n            f\"Refusing to write {label} containing invalid Unicode: {name!r}\"\n        ) from e\n    if encoded_length > MAX_COMPONENT_LENGTH:\n        raise UnsafePathComponentError(\n            f\"Refusing to write {label} longer than {MAX_COMPONENT_LENGTH} bytes: \"\n            f\"{basename[:32]!r}... ({encoded_length} bytes)\"\n        )\n    # Compare everything before the first dot: on Windows `NUL.tar.gz` opens\n    # the null device just as `NUL` does, so any number of extensions provides\n    # no protection.\n    device_name = basename.split(\".\", 1)[0].rstrip(\" \").upper()\n    if device_name in WINDOWS_RESERVED_NAMES:\n        raise UnsafePathComponentError(\n            f\"Refusing to write {label} that is a reserved device name: {name!r}\"\n        )\n    return basename\n\n","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/utils/safe_path.py#L176-L212","documentation":"safe_basename catches UnicodeEncodeError from os.fsencode(basename) — the filename contains characters that cannot be encoded with the filesystem encoding (e.g. surrogates from decoding bytes with errors='replace' on a UTF-8 fs) and is refused rather than crashing at write time.","triggerScenarios":"secure_basename_join(base, name) where name contains lone surrogate code points (\\udcXX) — typical when arbitrary bytes were decoded with surrogateescape and the locale/filesystem encoding can't represent them.","commonSituations":"See trigger scenarios.","solutions":["Re-encode lossily: name.encode('utf-8', 'replace').decode('utf-8') to drop unencodable characters","Ensure the process runs with a UTF-8 filesystem encoding (LANG/LC_ALL, PYTHONUTF8=1)","Generate an ASCII fallback filename for unencodable input"],"exampleFix":"# before\nsecure_basename_join(base, name)\n# after\nname = name.encode('utf-8', 'replace').decode('utf-8')\nsecure_basename_join(base, name)","handlingStrategy":"fallback","validationCode":"def encodable(v):\n    try:\n        os.fsencode(v)\n        return True\n    except UnicodeEncodeError:\n        return False","typeGuard":"import os\ndef is_fs_encodable(v: str) -> bool:\n    try:\n        os.fsencode(v); return True\n    except UnicodeEncodeError:\n        return False","tryCatchPattern":"from composio.exceptions import UnsafePathComponentError\ntry:\n    p = secure_basename_join(base, name)\nexcept UnsafePathComponentError:\n    p = secure_basename_join(base, name.encode('utf-8','replace').decode('utf-8'))","preventionTips":["Run with UTF-8 filesystem encoding (PYTHONUTF8=1)","Normalize surrogate-containing strings with errors='replace' before download"],"tags":["unicode","filename-validation","python"],"backgroundTag":"unicode-encode-error-filename","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}