{"record":{"id":"b09ca2d0c983d948","repo":"github/spec-kit","slug":"error-number-must-be-a-non-negative-integer","errorCode":null,"errorMessage":"Error: --number must be a non-negative integer","messagePattern":"Error: --number must be a non-negative integer","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"extensions/git/scripts/python/create_new_feature_branch.py","lineNumber":115,"sourceCode":"        elif arg == \"--dry-run\":\n            args.dry_run = True\n        elif arg == \"--allow-existing-branch\":\n            args.allow_existing = True\n        elif arg == \"--short-name\":\n            if i + 1 >= len(argv) or argv[i + 1].startswith(\"--\"):\n                _err(\"Error: --short-name requires a value\")\n                raise SystemExit(1)\n            i += 1\n            args.short_name = argv[i]\n        elif arg == \"--number\":\n            if i + 1 >= len(argv) or argv[i + 1].startswith(\"--\"):\n                _err(\"Error: --number requires a value\")\n                raise SystemExit(1)\n            i += 1\n            args.branch_number = argv[i]\n            if not re.fullmatch(r\"[0-9]+\", args.branch_number):\n                _err(\"Error: --number must be a non-negative integer\")\n                raise SystemExit(1)\n        elif arg == \"--timestamp\":\n            args.use_timestamp = True\n        elif arg in (\"--help\", \"-h\"):\n            print(HELP_TEXT)\n            raise SystemExit(0)\n        else:\n            args.description_parts.append(arg)\n        i += 1\n    return args\n\n\n# ── Core helpers loading ─────────────────────────────────────────────────────\n\n\ndef _find_project_root(start: Path) -> Path | None:\n    current = start\n    while True:\n        if (current / \".specify\").is_dir() or (current / \".git\").exists():","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/extensions/git/scripts/python/create_new_feature_branch.py#L97-L133","documentation":"During the directory walk, an existing component resolved (Path.resolve) to a location outside the resolved project root. This fires when the path is not itself a symlink but still lands outside the root — e.g. a hardlink-style bind mount, a mount point, or a symlink deeper in the chain that only resolve() exposes. The write is refused to keep shared infra inside the project.","triggerScenarios":"An intermediate directory under project_path is a mount point or was created via bind mount pointing elsewhere; or the component is a junction (Windows) that is_symlink() does not flag but resolve() relocates outside root; or project_path itself is passed unresolved while another component resolves through a link.","commonSituations":"Devcontainers/WSL where directories are bind-mounted from the Windows host; macOS Finder aliases or network mounts placed inside the repo; running with a project_path that is itself under a symlink (e.g. /tmp -> /private/tmp) so root resolution disagrees with component resolution.","solutions":["Pass the fully resolved root: use Path(project_path).resolve() before calling the API so root and components agree","Replace mounts/junctions inside the project tree with real directories containing the actual data","On WSL/containers, ensure the checkout lives on a native filesystem path, not a mounted host path with redirecting semantics","Reproduce with Path(p).resolve() on each component in a REPL to find which one leaves the root"],"exampleFix":"# before\nrun(project_path=Path('/tmp/proj'))  # /tmp is a symlink on macOS\n\n# after\nrun(project_path=Path('/tmp/proj').resolve())  # /private/tmp/proj, consistent with resolve() inside","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\nroot = project_path.resolve()\nfor cur in [root] + [root.joinpath(*rel_dir.parts[:i+1]) for i in range(len(rel_dir.parts))]:\n    if cur.exists() and not cur.is_symlink():\n        assert cur.resolve().is_relative_to(root), f'{cur} resolves outside root'","typeGuard":null,"tryCatchPattern":"try:\n    _ensure_safe_shared_directory(project_path, directory)\nexcept ValueError as e:\n    if 'escapes project root' in str(e):\n        # pass a resolved root, or remove mounts/junctions inside the tree\n        raise\n    raise","preventionTips":["Always pass Path(project_path).resolve() to shared-infra APIs","Avoid bind mounts, junctions, and mount points inside the project tree","On macOS/WSL, prefer native paths over /tmp or /mnt host redirects"],"tags":["filesystem","symlink","security","portability"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}