{"record":{"id":"f0e3223e70a1aad6","repo":"ComposioHQ/composio","slug":"cache-directory-directory-is-not-writable-please","errorCode":null,"errorMessage":"Cache directory {directory} is not writable please provide a path that is writable using {ENV_LOCAL_CACHE_DIRECTORY} environment variable.","messagePattern":"Cache directory (.+?) is not writable please provide a path that is writable using (.+?) environment variable\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/composio/core/models/_files.py","lineNumber":145,"sourceCode":"\n\ndef ensure_cache_directory() -> Path:\n    \"\"\"Create the cache directory on first use and check that it is writable.\n\n    This used to run at module import time, so a bare ``import composio``\n    raised ``RuntimeError`` on any read-only filesystem -- AWS Lambda,\n    distroless containers, ``ProtectHome=true`` systemd units -- even for\n    programs that never touched a file. Deferring it to first use keeps the\n    same check, and the same error message, for the callers that actually\n    need the directory.\n    \"\"\"\n    directory = get_cache_directory()\n    try:\n        directory.mkdir(parents=True, exist_ok=True)\n        if not os.access(directory, os.W_OK):\n            raise OSError\n    except OSError as e:\n        raise RuntimeError(\n            f\"Cache directory {directory} is not writable please \"\n            f\"provide a path that is writable using {ENV_LOCAL_CACHE_DIRECTORY} \"\n            \"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()","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/core/models/_files.py#L127-L163","documentation":"A custom tool's final slug (after the SDK applies prefixing/suffixing rules) exceeds MAX_SLUG_LENGTH characters. The slug length is enforced on the computed final slug, not just the handle slug you provided, so short-looking slugs can still overflow once toolkit prefixes are appended.","triggerScenarios":"Registering a CustomTool whose handle.slug, once combined with its toolkit slug/prefix, produces a final slug longer than MAX_SLUG_LENGTH; occurs in build_custom_tools_map / build_custom_tools_map_from_response during create or use.","commonSituations":"Long descriptive tool names like 'AGENT_ANALYZE_CUSTOMER_SUPPORT_TICKETS_V2'; nesting tools under a toolkit with a long slug; generated slugs from function names.","solutions":["Shorten the custom tool's slug","Shorten the parent toolkit slug if the prefix is what pushes it over","Split the tool into multiple shorter-named tools"],"exampleFix":"# before\ntool = CustomTool(slug='VERY_LONG_TOOL_NAME_THAT_EXCEEDS_THE_LIMIT...', ...)\n# after\ntool = CustomTool(slug='ANALYZE_TICKETS', ...)","handlingStrategy":"validation","validationCode":"from composio.core.models.custom_tool import MAX_SLUG_LENGTH\n\nfinal = f'{toolkit_slug}_{handle.slug}' if toolkit_slug else handle.slug\nif len(final) > MAX_SLUG_LENGTH:\n    handle.slug = handle.slug[:MAX_SLUG_LENGTH - len(toolkit_slug) - 1]","typeGuard":"def slug_fits(handle_slug: str, toolkit_prefix: str = '') -> bool:\n    final = f'{toolkit_prefix}{handle_slug}' if toolkit_prefix else handle_slug\n    return len(final) <= MAX_SLUG_LENGTH","tryCatchPattern":"try:\n    tools_map = build_custom_tools_map(tools, toolkits)\nexcept ValidationError as e:\n    if 'exceeds' in str(e) and 'characters' in str(e):\n        # shorten slugs and rebuild\n        shorten_slugs(tools)\n        tools_map = build_custom_tools_map(tools, toolkits)\n    else:\n        raise","preventionTips":["Keep custom tool slugs short (<= 40 chars) by convention","Account for toolkit slug prefixes when choosing tool slugs"],"tags":["python","custom-tools","slug","validation"],"backgroundTag":"identifier-too-long","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}