ComposioHQ/composio · error · AttributeError
module {__name__!r} has no attribute {name!r}
Error message
module {__name__!r} has no attribute {name!r} What it means
Two custom tools resolve to the same final slug, so the registry cannot uniquely identify them. Final slugs are matched case-insensitively, so 'MyTool' and 'mytool' also collide.
Source
Thrown at python/composio/core/models/_files.py:166
"environment variable."
) from e
return directory
def __getattr__(name: str) -> Path:
"""Keep the historical module-level path constants working, but lazily.
``LOCAL_CACHE_DIRECTORY`` and ``LOCAL_OUTPUT_FILE_DIRECTORY`` used to be
computed at import time. They are now resolved on attribute access
instead (PEP 562), so importing this module no longer touches the
filesystem or depends on the environment, and both constants observe a
``COMPOSIO_CACHE_DIR`` that was set after import.
"""
if name == "LOCAL_CACHE_DIRECTORY":
return get_cache_directory()
if name == "LOCAL_OUTPUT_FILE_DIRECTORY":
return get_output_file_directory()
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def get_md5(file: Path) -> str:
"""Calculate MD5 hash of a file for integrity verification.
Note: MD5 is used here for file integrity checking and deduplication,
not for cryptographic security. The Composio API requires MD5 hashes
for file upload verification. For security-critical applications,
consider using SHA-256 for additional integrity checks.
Args:
file: Path to file to hash
Returns:
Hexadecimal MD5 hash string
"""
# `usedforsecurity=False` lets this run on FIPS-mode systems, where
# `hashlib.md5()` without the flag raises `ValueError: [digital envelopeView on GitHub (pinned to 64b1b85502)
Solutions
- Rename one of the colliding custom tool slugs
- Remove the duplicate registration (same tool added twice)
- Check case-insensitive matches (FOO vs foo) when auditing slugs
Example fix
# before tools = [CustomTool(slug='SEARCH', ...), CustomTool(slug='search', ...)] # after tools = [CustomTool(slug='SEARCH_DOCS', ...), CustomTool(slug='SEARCH_ISSUES', ...)]
Defensive patterns
Strategy: validation
Validate before calling
finals = [final_slug_for(h, tk) for h, tk in all_handles]
keys = [f.upper() for f in finals]
if len(keys) != len(set(keys)):
dupes = {k for k in keys if keys.count(k) > 1}
raise ValueError(f'Duplicate final slugs: {dupes}') Type guard
def no_final_slug_collisions(final_slugs: list[str]) -> bool:
keys = [s.upper() for s in final_slugs]
return len(keys) == len(set(keys)) Try / catch
try:
tools_map = build_custom_tools_map(tools, toolkits)
except ValidationError as e:
if 'already registered' in str(e):
# rename duplicates deterministically and retry once
dedupe_and_rename(tools)
tools_map = build_custom_tools_map(tools, toolkits)
else:
raise Prevention
- Compare slugs case-insensitively when auditing for duplicates
- Namespace tool slugs by domain or toolkit
When it happens
Trigger: Registering two CustomTool handles (across tools and toolkits) whose computed final slugs match case-insensitively, via build_custom_tools_map or build_custom_tools_map_from_response (e.g. composio.tools.create / use).
Common situations: Registering the same tool twice; tools named the same in two toolkits; casing differences between slugs; merging tool lists from multiple modules.
Related errors
- Failed to upload to S3. Status: {response.status_code}. This
- Cache directory {directory} is not writable please provide a
- Failed to upload to S3: {_sanitize_url_for_logging(url)}. Er
- Request timed out fetching URL: {_sanitize_url_for_logging(u
- 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/aefe8fbc1f4fe2ba.
Report an issue: GitHub.