ComposioHQ/composio · error · UnsafePathComponentError
Refusing to build a path from an empty or non-string {label}
Error message
Refusing to build a path from an empty or non-string {label}: {value!r} What it means
assert_safe_path_component refuses to build a path from a value that is not a non-empty string (None, int, empty ''). All untrusted path components (slugs, IDs) must be non-empty strings before they can join a filesystem path.
Source
Thrown at python/composio/utils/safe_path.py:101
sep = os.sep
parent_with_sep = parent_str if parent_str.endswith(sep) else parent_str + sep
return child_str.startswith(parent_with_sep)
except OSError:
return False
def assert_safe_path_component(value: str, *, label: str = "path component") -> str:
"""Return ``value`` unchanged if it is safe to use as a single path
component, else raise.
Fails closed. Rejects traversal (``..``), separators of either platform,
absolute paths, drive letters, NUL bytes, reserved device names, and
anything outside :data:`SAFE_COMPONENT_REGEX`.
: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)"View on GitHub (pinned to 64b1b85502)
Solutions
- Check for None/empty before joining and skip or default the operation
- Coerce non-string identifiers with str(...) only if that is genuinely the intended value
- Fix the caller that produced None (missing field in request payload)
Example fix
# before
path = secure_join(root, tool.slug) # slug is None
# after
if not tool.slug:
raise ValueError("tool slug missing")
path = secure_join(root, tool.slug) Defensive patterns
Strategy: type-guard
Validate before calling
def usable_component(v):
return isinstance(v, str) and len(v) > 0 Type guard
def is_safe_component(v) -> bool:
return isinstance(v, str) and bool(v) Try / catch
from composio.exceptions import UnsafePathComponentError
try:
p = secure_join(root, slug)
except UnsafePathComponentError:
slug = fallback_slug() # e.g. derived from a hash
p = secure_join(root, slug) Prevention
- Check required API fields for presence before path building
- Never pass raw optional fields into path helpers
When it happens
Trigger: Calling secure_join(root, component) where component is None (e.g. a missing API field), an empty string, or a non-string like an int ID.
Common situations: API responses with missing/optional slug or ID fields passed straight into secure_join; deserialized JSON where the field is absent rather than empty.
Related errors
- Unsafe path component: {e}
- Refusing to build a path from a {label} containing path sepa
- Path traversal detected: {label} {name!r} leaves no usable b
- Path traversal detected: {label} {name!r} resolves to {candi
- Refusing to write outside the configured directory: {compone
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/8444a64ae29c2db7.
Report an issue: GitHub.