{"record":{"id":"23e27fb41e2e00b2","repo":"ComposioHQ/composio","slug":"refusing-to-write-label-longer-than-max-compone","errorCode":null,"errorMessage":"Refusing to write {label} longer than {MAX_COMPONENT_LENGTH} bytes: {basename[:32]!r}... ({encoded_length} bytes)","messagePattern":"Refusing to write (.+?) longer than (.+?) bytes: (.+?)\\.\\.\\. \\((.+?) bytes\\)","errorType":"exception","errorClass":"UnsafePathComponentError","httpStatus":null,"severity":"error","filePath":"python/composio/utils/safe_path.py","lineNumber":198,"sourceCode":"    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\ndef resolve_root(root: t.Union[str, Path]) -> Path:\n    \"\"\"Normalize a trusted root to an absolute, symlink-resolved path.\n\n    Every containment check must derive its anchor through this one function.","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/utils/safe_path.py#L180-L216","documentation":"safe_basename rejects filenames whose filesystem-encoded length exceeds MAX_COMPONENT_LENGTH (128) bytes. os.fsencode measures real bytes, so multi-byte UTF-8 names hit the cap sooner than their character count suggests, keeping writes safely under common 255-byte filename limits.","triggerScenarios":"secure_basename_join(base, name) where len(os.fsencode(name)) > 128 — long descriptive filenames, CJK filenames (3 bytes/char), or hash-based names.","commonSituations":"Generated filenames embedding full titles or URLs; Unicode-heavy locales inflating byte length; download names composed from several API fields.","solutions":["Truncate the filename to a byte budget preserving the extension","Replace long names with a short hash and keep the original in metadata"],"exampleFix":"# before\nsecure_basename_join(base, long_name)\n# after\nstem, _, ext = long_name.rpartition('.')\nbudget = 120 - len(ext.encode()) - 1\nlong_name = stem.encode('utf-8')[:budget].decode('utf-8', 'ignore') + '.' + ext\nsecure_basename_join(base, long_name)","handlingStrategy":"validation","validationCode":"import os\nMAX = 128\ndef within_byte_budget(v):\n    try: return len(os.fsencode(v)) <= MAX\n    except UnicodeEncodeError: return False","typeGuard":"import os\ndef is_short_filename(v: str) -> bool:\n    try:\n        return len(os.fsencode(v)) <= 128\n    except UnicodeEncodeError:\n        return False","tryCatchPattern":"from composio.exceptions import UnsafePathComponentError\ntry:\n    p = secure_basename_join(base, name)\nexcept UnsafePathComponentError:\n    stem, _, ext = name.rpartition('.')\n    p = secure_basename_join(base, stem[:64] + ('.' + ext if ext else ''))","preventionTips":["Measure filename length in bytes, not characters","Prefer short hash-based filenames; keep original names in metadata"],"tags":["filename-length","python"],"backgroundTag":"filename-too-long","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}