{"record":{"id":"aefe8fbc1f4fe2ba","repo":"ComposioHQ/composio","slug":"module-name-r-has-no-attribute-name-r","errorCode":null,"errorMessage":"module {__name__!r} has no attribute {name!r}","messagePattern":"module (.+?) has no attribute (.+?)","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"python/composio/core/models/_files.py","lineNumber":166,"sourceCode":"            \"environment variable.\"\n        ) from e\n    return directory\n\n\ndef __getattr__(name: str) -> Path:\n    \"\"\"Keep the historical module-level path constants working, but lazily.\n\n    ``LOCAL_CACHE_DIRECTORY`` and ``LOCAL_OUTPUT_FILE_DIRECTORY`` used to be\n    computed at import time. They are now resolved on attribute access\n    instead (PEP 562), so importing this module no longer touches the\n    filesystem or depends on the environment, and both constants observe a\n    ``COMPOSIO_CACHE_DIR`` that was set after import.\n    \"\"\"\n    if name == \"LOCAL_CACHE_DIRECTORY\":\n        return get_cache_directory()\n    if name == \"LOCAL_OUTPUT_FILE_DIRECTORY\":\n        return get_output_file_directory()\n    raise AttributeError(f\"module {__name__!r} has no attribute {name!r}\")\n\n\ndef get_md5(file: Path) -> str:\n    \"\"\"Calculate MD5 hash of a file for integrity verification.\n\n    Note: MD5 is used here for file integrity checking and deduplication,\n    not for cryptographic security. The Composio API requires MD5 hashes\n    for file upload verification. For security-critical applications,\n    consider using SHA-256 for additional integrity checks.\n\n    Args:\n        file: Path to file to hash\n\n    Returns:\n        Hexadecimal MD5 hash string\n    \"\"\"\n    # `usedforsecurity=False` lets this run on FIPS-mode systems, where\n    # `hashlib.md5()` without the flag raises `ValueError: [digital envelope","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/core/models/_files.py#L148-L184","documentation":"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.","triggerScenarios":"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).","commonSituations":"Registering the same tool twice; tools named the same in two toolkits; casing differences between slugs; merging tool lists from multiple modules.","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"],"exampleFix":"# before\ntools = [CustomTool(slug='SEARCH', ...), CustomTool(slug='search', ...)]\n# after\ntools = [CustomTool(slug='SEARCH_DOCS', ...), CustomTool(slug='SEARCH_ISSUES', ...)]","handlingStrategy":"validation","validationCode":"finals = [final_slug_for(h, tk) for h, tk in all_handles]\nkeys = [f.upper() for f in finals]\nif len(keys) != len(set(keys)):\n    dupes = {k for k in keys if keys.count(k) > 1}\n    raise ValueError(f'Duplicate final slugs: {dupes}')","typeGuard":"def no_final_slug_collisions(final_slugs: list[str]) -> bool:\n    keys = [s.upper() for s in final_slugs]\n    return len(keys) == len(set(keys))","tryCatchPattern":"try:\n    tools_map = build_custom_tools_map(tools, toolkits)\nexcept ValidationError as e:\n    if 'already registered' in str(e):\n        # rename duplicates deterministically and retry once\n        dedupe_and_rename(tools)\n        tools_map = build_custom_tools_map(tools, toolkits)\n    else:\n        raise","preventionTips":["Compare slugs case-insensitively when auditing for duplicates","Namespace tool slugs by domain or toolkit"],"tags":["python","custom-tools","slug","duplicate","validation"],"backgroundTag":"duplicate-identifier","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}