{"record":{"id":"40409395f2c4c92b","repo":"ComposioHQ/composio","slug":"context-slug-must-only-contain-alphanumeric-cha","errorCode":null,"errorMessage":"{context}: slug must only contain alphanumeric characters, underscores, and hyphens","messagePattern":"(.+?): slug must only contain alphanumeric characters, underscores, and hyphens","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"python/composio/core/models/custom_tool.py","lineNumber":87,"sourceCode":"        Experimental as SessionCreateResponseExperimental,\n    )\n    from composio_client.types.tool_router.session_retrieve_response import (\n        Experimental as SessionRetrieveResponseExperimental,\n    )\n\n\n# ────────────────────────────────────────────────────────────────\n# Slug validation helpers\n# ────────────────────────────────────────────────────────────────\n\n\ndef _validate_slug(slug: str, context: str) -> str:\n    \"\"\"Validate a custom tool or toolkit slug.\"\"\"\n    if not slug:\n        raise ValidationError(f\"{context}: slug is required\")\n\n    if not SLUG_REGEX.match(slug):\n        raise ValidationError(\n            f\"{context}: slug must only contain alphanumeric characters, \"\n            f\"underscores, and hyphens\"\n        )\n\n    upper = slug.upper()\n    if upper.startswith(\"LOCAL_\"):\n        raise ValidationError(\n            f'{context}: slug must not start with \"LOCAL_\" — '\n            f\"this prefix is reserved for internal routing.\"\n        )\n    if upper.startswith(\"COMPOSIO_\"):\n        raise ValidationError(\n            f'{context}: slug must not start with \"COMPOSIO_\" — '\n            f\"this prefix is reserved for Composio meta tools.\"\n        )\n\n    return slug\n","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/core/models/custom_tool.py#L69-L105","documentation":"Raised when a custom tool/toolkit slug contains characters outside [A-Za-z0-9_-]. Slugs are embedded in routed tool names sent to the backend, so they must match the strict SLUG_REGEX.","triggerScenarios":"Passing a slug with spaces, dots, slashes, unicode, or other symbols, e.g. slug='search.docs' or slug='search docs'; auto-derived slugs from docstrings or filenames containing invalid characters.","commonSituations":"Slugifying titles with a function that allows dots or spaces; using paths or UUIDs as slugs; non-ASCII tool names from localization.","solutions":["Sanitize the slug to only alphanumeric, underscore, and hyphen characters (e.g. re.sub(r'[^a-zA-Z0-9_-]', '_', slug))","Replace spaces with underscores or hyphens manually before creating the tool","If generating from arbitrary text, use a proper slugify helper and verify against ^[a-zA-Z0-9_-]+$ before passing it in"],"exampleFix":"# before\nTool(slug=\"search.docs\", ...)\n\n# after\nimport re\nslug = re.sub(r\"[^a-zA-Z0-9_-]\", \"_\", \"search.docs\")  # 'search_docs'\nTool(slug=slug, ...)","handlingStrategy":"validation","validationCode":"import re\ndef slugify(s: str) -> str:\n    return re.sub(r\"[^a-zA-Z0-9_-]\", \"_\", s.strip()) or \"tool\"","typeGuard":"def is_valid_slug(slug: str) -> bool:\n    import re\n    return bool(re.fullmatch(r\"[a-zA-Z0-9_-]+\", slug))","tryCatchPattern":"try:\n    Tool(slug=slug, ...)\nexcept ValidationError:\n    Tool(slug=slugify(slug), ...)","preventionTips":["Run slugs through a slugify helper before registration","Never pass raw titles, paths, or unicode strings as slugs","Add a unit test asserting generated slugs match ^[a-zA-Z0-9_-]+$"],"tags":["python","custom-tools","slug-validation"],"backgroundTag":"slug-validation-failed","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}