{"record":{"id":"d889da188b85b2a1","repo":"ComposioHQ/composio","slug":"context-name-is-required","errorCode":null,"errorMessage":"{context}: name is required","messagePattern":"(.+?): name is required","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"python/composio/core/models/custom_tool.py","lineNumber":176,"sourceCode":"\ndef _create_tool(\n    slug: str,\n    *,\n    name: str,\n    description: str,\n    input_params: t.Type[BaseModel],\n    execute: CustomToolExecuteFn,\n    extends_toolkit: t.Optional[str] = None,\n    output_params: t.Optional[t.Type[BaseModel]] = None,\n    preload: t.Optional[bool] = None,\n) -> CustomTool:\n    \"\"\"Internal: create and validate a CustomTool.\"\"\"\n    context = \"experimental.tool\"\n\n    _validate_slug(slug, context)\n\n    if not name:\n        raise ValidationError(f\"{context}: name is required\")\n    if not description:\n        raise ValidationError(f\"{context}: description is required\")\n\n    if not isinstance(input_params, type) or not issubclass(input_params, BaseModel):\n        raise ValidationError(\n            f\"{context}: input_params must be a Pydantic BaseModel subclass. \"\n            f\"Tool input parameters are always an object with named properties.\"\n        )\n\n    try:\n        from pydantic import RootModel\n\n        if issubclass(input_params, RootModel):\n            raise ValidationError(\n                f\"{context}: input_params must be a regular BaseModel with named fields, \"\n                f\"not a RootModel. Tool input parameters are always an object with \"\n                f\"named properties.\"\n            )","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/core/models/custom_tool.py#L158-L194","documentation":"Raised when creating a custom tool with an empty name. The display name is a required field for tool registration and is surfaced to LLMs as the tool label.","triggerScenarios":"Calling Tool(...) (or the @tool decorator / _create_tool path) with name=\"\" or a name that ends up empty after defaults are applied.","commonSituations":"Omitting name expecting it to default to the function name; passing name=None where the signature doesn't default it; data-driven tool definitions where a name field is missing in the source dict/config.","solutions":["Provide an explicit non-empty name, e.g. name='Search documentation'","When generating tools from functions, default name to fn.__name__.replace('_', ' ').title() before creation","Validate tool definition dicts/configs before passing them to Tool(...)"],"exampleFix":"# before\nTool(slug=\"search\", name=\"\", description=\"Search\", input_params=P, callable=fn)\n\n# after\nTool(slug=\"search\", name=\"Search\", description=\"Search\", input_params=P, callable=fn)","handlingStrategy":"validation","validationCode":"if not name:\n    name = getattr(fn, \"__name__\", \"tool\").replace(\"_\", \" \").title()\nassert name, \"tool name required\"","typeGuard":"def has_name(name: str | None) -> bool:\n    return bool(name and name.strip())","tryCatchPattern":"try:\n    Tool(name=name, ...)\nexcept ValidationError:\n    Tool(name=fn.__name__.replace(\"_\", \" \").title(), ...)","preventionTips":["Always set an explicit human-readable name","Default name from the function name when generating tools","Validate config-driven tool definitions before registration"],"tags":["python","custom-tools","required-field"],"backgroundTag":"missing-required-field","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}