{"record":{"id":"e366c2264e5d6518","repo":"shareAI-lab/learn-claude-code","slug":"invalid-worktree-name-use-1-40-chars-letters-nu","errorCode":null,"errorMessage":"Invalid worktree name. Use 1-40 chars: letters, numbers, ., _, -","messagePattern":"Invalid worktree name\\. Use 1-40 chars: letters, numbers, \\., _, -","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agents/s12_worktree_task_isolation.py","lineNumber":280,"sourceCode":"            raise RuntimeError(msg or f\"git {' '.join(args)} failed\")\n        return (r.stdout + r.stderr).strip() or \"(no output)\"\n\n    def _load_index(self) -> dict:\n        return json.loads(self.index_path.read_text())\n\n    def _save_index(self, data: dict):\n        self.index_path.write_text(json.dumps(data, indent=2))\n\n    def _find(self, name: str) -> dict | None:\n        idx = self._load_index()\n        for wt in idx.get(\"worktrees\", []):\n            if wt.get(\"name\") == name:\n                return wt\n        return None\n\n    def _validate_name(self, name: str):\n        if not re.fullmatch(r\"[A-Za-z0-9._-]{1,40}\", name or \"\"):\n            raise ValueError(\n                \"Invalid worktree name. Use 1-40 chars: letters, numbers, ., _, -\"\n            )\n\n    def create(self, name: str, task_id: int = None, base_ref: str = \"HEAD\") -> str:\n        self._validate_name(name)\n        if self._find(name):\n            raise ValueError(f\"Worktree '{name}' already exists in index\")\n        if task_id is not None and not self.tasks.exists(task_id):\n            raise ValueError(f\"Task {task_id} not found\")\n\n        path = self.dir / name\n        branch = f\"wt/{name}\"\n        self.events.emit(\n            \"worktree.create.before\",\n            task={\"id\": task_id} if task_id is not None else {},\n            worktree={\"name\": name, \"base_ref\": base_ref},\n        )\n        try:","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/agents/s12_worktree_task_isolation.py#L262-L298","documentation":"Thrown by WorktreeManager._validate_name when creating a worktree whose name fails the regex [A-Za-z0-9._-]{1,40}. The name is used both as a directory name under the worktree dir and inside the branch name wt/{name}, so any character outside that whitelist (spaces, slashes, unicode) or a name longer than 40 chars (or empty/None) is rejected before any git command runs. This is a fail-fast input validation guard, not a git failure.","triggerScenarios":"Calling worktree create with name=\"my task\" (space), name=\"feature/fix\" (slash), name=\"задача\" (unicode), name=\"\" or None, or a name of 41+ characters. The regex uses fullmatch, so even a trailing newline embedded in the string fails.","commonSituations":"Agent pipelines auto-deriving worktree names from free-form task subjects or ticket titles without sanitizing; names containing '/' that would also collide with the wt/{name} branch namespace; trailing whitespace from user input not being stripped before the call.","solutions":["Sanitize the name before calling create(): lower-case it and re.sub(r'[^A-Za-z0-9._-]+', '-', name), then trim to 40 chars","Strip whitespace around the name and reject empty results before calling the manager","Replace '/' in derived names (e.g. from branch paths) with '-' so the wt/{name} branch stays well-formed","Catch ValueError at the tool boundary and surface it back to the caller so the agent can retry with a cleaned name"],"exampleFix":"// before\nwt.create(name=\"Fix login bug / session\", task_id=7)\n// after\nimport re\nname = re.sub(r'[^A-Za-z0-9._-]+', '-', \"Fix login bug / session\").strip('-.')[:40]\nwt.create(name=name, task_id=7)","handlingStrategy":"validation","validationCode":"import re\n\ndef valid_worktree_name(name: str) -> bool:\n    return bool(re.fullmatch(r\"[A-Za-z0-9._-]{1,40}\", name or \"\"))\n\n# before create():\nassert valid_worktree_name(name), \"name must be 1-40 chars of [A-Za-z0-9._-]\"","typeGuard":"def is_valid_worktree_name(name: object) -> bool:\n    return isinstance(name, str) and bool(__import__('re').fullmatch(r\"[A-Za-z0-9._-]{1,40}\", name))","tryCatchPattern":"try:\n    path = wt.create(name, task_id=tid)\nexcept ValueError as e:\n    if 'Invalid worktree name' in str(e):\n        name = re.sub(r'[^A-Za-z0-9._-]+', '-', name).strip('-.')[:40]\n        path = wt.create(name, task_id=tid)\n    else:\n        raise","preventionTips":["Sanitize derived names (task subjects, branch names) with a slug function before create()","Strip and reject empty names client-side","Never let free-form LLM output reach create() unfiltered"],"tags":["worktree","validation","git","input-sanitization"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}