{"record":{"id":"776b60d3c128f78e","repo":"can1357/oh-my-pi","slug":"invalid-pr-number-pr-number-r","errorCode":null,"errorMessage":"invalid PR number: {pr_number!r}","messagePattern":"invalid PR number: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/robomp/src/git_ops.py","lineNumber":626,"sourceCode":"def fetch_pr_head(\n    repo_dir: Path,\n    pr_number: int,\n    *,\n    token: str | None,\n    remote_url: str | None = None,\n    auth_url: str | None = None,\n    safe_directory: Path | None = None,\n) -> None:\n    \"\"\"Fetch ``refs/pull/<n>/head`` into FETCH_HEAD with all reachable blobs.\n\n    Immediately followed by ``git worktree add --detach FETCH_HEAD`` for PR\n    review checkouts. See :func:`fetch_ref` for why ``--refetch --no-filter``\n    is required: without the blob backfill, the worktree-add triggers a\n    promisor lazy fetch that fails under proxy-transport deployments\n    (oh-my-pi#1818).\n    \"\"\"\n    if pr_number <= 0:\n        raise ValueError(f\"invalid PR number: {pr_number!r}\")\n    remote = remote_url or \"origin\"\n    ref = f\"refs/pull/{pr_number}/head\" if remote_url else f\"pull/{pr_number}/head\"\n    args = [\"fetch\", \"--refetch\", \"--no-filter\", remote, ref]\n    _check(\n        _run_git(\n            args,\n            cwd=repo_dir,\n            token=token,\n            auth_url=auth_url,\n            extra_env=_explicit_remote_env(remote_url, cwd=repo_dir),\n            safe_directory=safe_directory,\n        ),\n        [\"git\", *args],\n    )\n\n\n@dataclass(slots=True, frozen=True)\nclass PushResult:","sourceCodeStart":608,"sourceCodeEnd":644,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/git_ops.py#L608-L644","documentation":"`fetch_pr_head` validates that `pr_number` is a positive integer and raises this ValueError otherwise. PR refs map to `refs/pull/{n}/head`, so zero, negative numbers, or (from untyped callers) non-numeric values cannot produce a valid ref and are rejected before spawning git.","triggerScenarios":"Calling `fetch_pr_head(repo_dir, pr_number=0)`, a negative number, or a value like `\"#123\"`/`None` that reaches the `pr_number <= 0` check.","commonSituations":"Parsing PR numbers out of branch names or URLs with a regex that captures empty/garbage groups, off-by-one loops starting at 0, API responses where the number field was missing.","solutions":["Pass the actual positive PR number as an int","Sanitize the source of the number — extract digits from URLs like /pull/123 before calling","Validate input at your API boundary: `if not isinstance(n, int) or n <= 0: skip`"],"exampleFix":"// before\nnum = int(re.search(r\"pull/(\\d*)\", url).group(1))  # may be 0/empty\nfetch_pr_head(repo_dir, num)\n// after\nm = re.search(r\"pull/(\\d+)\", url)\nif not m:\n    raise ValueError(f\"no PR number in {url!r}\")\nfetch_pr_head(repo_dir, int(m.group(1)))","handlingStrategy":"validation","validationCode":"def fetch_pr_checked(repo, pr_number):\n    if not isinstance(pr_number, int) or isinstance(pr_number, bool) or pr_number <= 0:\n        raise ValueError(f\"PR number must be a positive int, got {pr_number!r}\")\n    return fetch_pr_head(repo, pr_number=pr_number)","typeGuard":"def is_valid_pr_number(n) -> bool:\n    return isinstance(n, int) and not isinstance(n, bool) and n > 0","tryCatchPattern":"try:\n    fetch_pr_head(repo_dir, pr_number=n)\nexcept ValueError as exc:\n    log.warning(\"skipping bad PR reference: %s\", exc)","preventionTips":["Parse PR numbers with regexes that require at least one digit: r\"pull/(\\d+)\"","Coerce at the boundary (API/webhook) and reject non-positive values there","Don't loop PR numbers from 0 — start from 1"],"tags":["git","validation","input-validation"],"backgroundTag":"invalid-input-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}