{"record":{"id":"5451b56034a70643","repo":"ComposioHQ/composio","slug":"context-slug-is-required","errorCode":null,"errorMessage":"{context}: slug is required","messagePattern":"(.+?): slug is required","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"python/composio/core/models/custom_tool.py","lineNumber":84,"sourceCode":"        Experimental as SessionAttachResponseExperimental,\n    )\n    from composio_client.types.tool_router.session_create_response import (\n        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        )","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/core/models/custom_tool.py#L66-L102","documentation":"Raised by Composio's custom-tool slug validator when an empty or None slug is supplied. Every custom tool and toolkit needs a non-empty slug because it becomes part of the tool's routed name.","triggerScenarios":"Creating a custom tool/toolkit via Tool(...) or toolkit decorators with slug=\"\" or omitting it where no default is derived; calling _create_tool with an empty slug string.","commonSituations":"Copying an example and deleting the slug field; dynamically generating slugs from user data where the value can be empty; assuming the library auto-generates a slug from the function name when it doesn't in a given API path.","solutions":["Provide an explicit non-empty slug, e.g. slug=\"search_docs\"","If generating slugs programmatically, default to the function name (fn.__name__) when the computed slug is empty","Add a fallback/slugify step before tool creation so user input can never arrive empty"],"exampleFix":"# before\nTool(slug=\"\", name=\"Search\", description=\"Search docs\", input_params=SearchParams, callable=search)\n\n# after\nTool(slug=\"search_docs\", name=\"Search\", description=\"Search docs\", input_params=SearchParams, callable=search)","handlingStrategy":"validation","validationCode":"import re\nSLUG_RE = re.compile(r\"^[a-zA-Z0-9_-]+$\")\ndef safe_slug(slug: str, fallback: str) -> str:\n    s = (slug or fallback or \"\").strip()\n    if not s:\n        raise ValueError(\"slug required\")\n    return s","typeGuard":"def is_valid_slug(slug: str | None) -> bool:\n    return bool(slug) and bool(SLUG_RE.match(slug))","tryCatchPattern":"from composio.core.exceptions import ValidationError\ntry:\n    tool = Tool(slug=slug, ...)\nexcept ValidationError as e:\n    slug = slugify(slug or fn.__name__)\n    tool = Tool(slug=slug, ...)","preventionTips":["Always pass an explicit slug or derive one from fn.__name__","Slugify free-text names before tool creation","Assert slugs are non-empty in CI for generated tools"],"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"}