ComposioHQ/composio · error · UnsafePathComponentError
Refusing to write {label} containing characters reserved by
Error message
Refusing to write {label} containing characters reserved by Windows: {name!r} What it means
safe_basename rejects filenames containing characters Windows reserves — control characters (ord < 32) or any of <>:"|?*. Catching these on every platform prevents files that would be unwritable or unopenable when the workspace syncs to a Windows machine.
Source
Thrown at python/composio/utils/safe_path.py:181
is unsafe to write.
"""
if not isinstance(name, str):
raise UnsafePathComponentError(
f"Refusing to write a non-string {label}: {name!r}"
)
raw_basename = PureWindowsPath(name).name
if not raw_basename or not raw_basename.strip() or set(raw_basename) == {"."}:
raise UnsafePathComponentError(
f"Path traversal detected: {label} {name!r} leaves no usable "
"basename to write to."
)
if "\x00" in raw_basename:
raise UnsafePathComponentError(
f"Refusing to write {label} containing a NUL byte: {name!r}"
)
if any(ord(char) < 32 or char in '<>:"|?*' for char in raw_basename):
raise UnsafePathComponentError(
f"Refusing to write {label} containing characters reserved by "
f"Windows: {name!r}"
)
if raw_basename.endswith((" ", ".")):
raise UnsafePathComponentError(
f"Refusing to write {label} ending in a space or dot: {name!r}"
)
basename = raw_basename.strip()
try:
encoded_length = len(os.fsencode(basename))
except UnicodeEncodeError as e:
raise UnsafePathComponentError(
f"Refusing to write {label} containing invalid Unicode: {name!r}"
) from e
if encoded_length > MAX_COMPONENT_LENGTH:
raise UnsafePathComponentError(
f"Refusing to write {label} longer than {MAX_COMPONENT_LENGTH} bytes: "View on GitHub (pinned to 64b1b85502)
Solutions
- Sanitize by replacing reserved characters with '-' or '_' before download
- Generate your own filename and keep the original only in metadata
Example fix
# before secure_basename_join(base, 'report<1>.pdf') # after import re safe = re.sub(r'[<>:"|?*]', '_', 'report<1>.pdf') secure_basename_join(base, safe)
Defensive patterns
Strategy: validation
Validate before calling
import re
RESERVED = re.compile(r'[<>:"|?*\x00-\x1f]')
def windows_clean(v):
return isinstance(v, str) and not RESERVED.search(v) Type guard
import re
def is_windows_safe_name(v: str) -> bool:
return not re.search(r'[<>:"|?*\x00-\x1f]', v) Try / catch
from composio.exceptions import UnsafePathComponentError
import re
try:
p = secure_basename_join(base, name)
except UnsafePathComponentError:
p = secure_basename_join(base, re.sub(r'[<>:"|?*]', '_', name)) Prevention
- Sanitize filenames to a conservative charset (alnum, dot, dash, underscore)
- Assume files will eventually land on Windows
When it happens
Trigger: secure_basename_join(base, name) where name contains characters like 'report<1>.pdf', 'file|name', 'q?.txt', or embedded control chars.
Common situations: Filenames from Unix-only sources that never considered Windows; adversarial pipe/colon names; CSV/emoji control characters in filenames.
Related errors
- Refusing to write {label} ending in a space or dot: {name!r}
- Refusing to build a path from a reserved device name as {lab
- Refusing to write {label} containing invalid Unicode: {name!
- Refusing to write {label} that is a reserved device name: {n
- Could not determine a home directory to store the Composio c
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/617aae27701efcfa.
Report an issue: GitHub.