ComposioHQ/composio · error · UnsafePathComponentError

Refusing to build a path from a {label} longer than {MAX_COM

Error message

Refusing to build a path from a {label} longer than {MAX_COMPONENT_LENGTH} characters: {value[:32]!r}... ({len(value)} characters)

What it means

assert_safe_path_component rejects a single path component longer than MAX_COMPONENT_LENGTH (128 characters), keeping components well under the 255-byte filename limit of ext4/APFS/NTFS so a write never fails mid-operation with OSError.

Source

Thrown at python/composio/utils/safe_path.py:116

    :raises UnsafePathComponentError: when ``value`` is unsafe.
    """
    if not isinstance(value, str) or not value:
        raise UnsafePathComponentError(
            f"Refusing to build a path from an empty or non-string {label}: {value!r}"
        )

    # `PureWindowsPath` treats both `/` and `\` as separators, so a single check
    # catches `../x` and `..\x` regardless of the host platform. A slug crafted
    # for a Windows target must not slip through on a POSIX build machine.
    as_windows_path = PureWindowsPath(value)
    if len(as_windows_path.parts) != 1 or as_windows_path.anchor:
        raise UnsafePathComponentError(
            f"Refusing to build a path from a {label} containing path separators "
            f"or a drive letter: {value!r}"
        )

    if len(value) > MAX_COMPONENT_LENGTH:
        raise UnsafePathComponentError(
            f"Refusing to build a path from a {label} longer than "
            f"{MAX_COMPONENT_LENGTH} characters: {value[:32]!r}... "
            f"({len(value)} characters)"
        )

    # `.` and `..` are excluded by the regex (no `.` in the character class),
    # as are NUL bytes and every separator. The explicit checks above exist to
    # produce a precise error message rather than a generic pattern mismatch.
    #
    # `fullmatch`, not `match`: in a `match`, `$` also matches just before a
    # single trailing newline, so `"GMAIL\n"` would satisfy `^[A-Za-z0-9_-]+$`
    # and reach the filesystem with a control character in the name.
    if not SAFE_COMPONENT_REGEX.fullmatch(value):
        raise UnsafePathComponentError(
            f"Refusing to build a path from an unsafe {label}: {value!r}. "
            f"Expected only letters, digits, underscores, and hyphens "
            f"(pattern {SAFE_COMPONENT_REGEX.pattern})."
        )

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Hash or truncate long identifiers before using them as directory names (e.g. sha256 hex of the slug)
  2. Store the full identifier in a metadata file inside a short-named directory
  3. Report legitimate catalog slugs that exceed 128 chars

Example fix

# before
secure_join(root, very_long_slug)
# after
import hashlib
short = hashlib.sha256(very_long_slug.encode()).hexdigest()[:32]
secure_join(root, short)
Defensive patterns

Strategy: validation

Validate before calling

def component_within_limit(v):
    return isinstance(v, str) and 0 < len(v) <= 128

Type guard

def is_short_component(v) -> bool:
    return isinstance(v, str) and 0 < len(v) <= 128

Try / catch

from composio.exceptions import UnsafePathComponentError
import hashlib
try:
    p = secure_join(root, slug)
except UnsafePathComponentError:
    p = secure_join(root, hashlib.sha256(slug.encode()).hexdigest()[:32])

Prevention

When it happens

Trigger: secure_join(root, slug) where slug exceeds 128 chars — e.g. a generated ID, hash, or long tool/action name used as a directory name.

Common situations: Backend-generated composite IDs or namespaced slugs (tool__action__param hashes) used directly as cache/workspace directory names.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/716273d6b9a42180. Report an issue: GitHub.