{"record":{"id":"ceaa0a9cd889814a","repo":"can1357/oh-my-pi","slug":"expected-owner-repo-nn-or-https-github-com-owner","errorCode":null,"errorMessage":"expected owner/repo#NN or https://github.com/owner/repo/issues/NN, got {ref!r}","messagePattern":"expected owner/repo#NN or https://github\\.com/owner/repo/issues/NN, got (.+?)","errorType":"validation","errorClass":"InvalidIssueRef","httpStatus":null,"severity":"error","filePath":"python/robomp/src/manual_triage.py","lineNumber":56,"sourceCode":"        super().__init__(f\"{delivery_id} is already {state}\")\n\n\nclass ManualTriageTimeout(TimeoutError):\n    \"\"\"Raised when a manual CLI waiter stops before terminal state.\"\"\"\n\n    def __init__(self, delivery_id: str, state: str, timeout_seconds: float) -> None:\n        self.delivery_id = delivery_id\n        self.state = state\n        self.timeout_seconds = timeout_seconds\n        super().__init__(f\"{delivery_id} did not reach a terminal state within {timeout_seconds:g}s (state={state})\")\n\n\ndef parse_issue_ref(ref: str) -> tuple[str, int]:\n    \"\"\"Parse `owner/repo#NN` or a github issue url into `(\"owner/repo\", NN)`.\"\"\"\n    cleaned = ref.strip()\n    match = _ISSUE_REF.match(cleaned) or _ISSUE_URL.match(cleaned)\n    if match is None:\n        raise InvalidIssueRef(f\"expected owner/repo#NN or https://github.com/owner/repo/issues/NN, got {ref!r}\")\n    return f\"{match.group('owner')}/{match.group('repo')}\", int(match.group(\"number\"))\n\n\ndef manual_delivery_id(repo_full: str, number: int) -> str:\n    \"\"\"Stable delivery id for manually-triggered triage. Re-runs reuse it.\"\"\"\n    return f\"manual-{repo_full.replace('/', '__')}-{number}\"\n\n\nasync def build_issues_opened_payload(github: GitHubBackend, repo_full: str, number: int) -> dict[str, Any]:\n    \"\"\"Fetch the issue + repo metadata and synthesize an `issues.opened` payload.\"\"\"\n    issue = await github.get_issue(repo_full, number)\n    if issue.is_pull_request:\n        raise ManualTriageError(f\"{repo_full}#{number} is a pull request, not an issue\")\n    repo = await github.get_repo(repo_full)\n    return {\n        \"action\": \"opened\",\n        \"issue\": {\n            \"number\": issue.number,","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/manual_triage.py#L38-L74","documentation":"InvalidIssueRef raised by parse_issue_ref when the input matches neither the `owner/repo#NN` pattern nor a GitHub issue URL. It is the module's validation gate for all manual triage entry points.","triggerScenarios":"api_trigger or triage receiving refs like \"123\", \"repo#12\", \"https://github.com/owner/repo/pull/12\", or whitespace-mangled strings.","commonSituations":"Users pasting PR URLs instead of issue URLs, bare issue numbers, or repo names without owner prefixes into the triage API/dashboard.","solutions":["Use full `owner/repo#123` format or `https://github.com/owner/repo/issues/123`","If input may be a PR link, convert `pull/NN` to `issues/NN` only if the item is actually an issue","Trim and validate the string before passing it in"],"exampleFix":"// before\ntriage(\"#123\")\n// after\ntriage(\"my-org/my-repo#123\")","handlingStrategy":"validation","validationCode":"import re\n_ISSUE_REF = re.compile(r\"^(?P<owner>[\\w.-]+)/(?P<repo>[\\w.-]+)#(?P<number>\\d+)$\")\n_ISSUE_URL = re.compile(r\"^https://github\\.com/(?P<owner>[\\w.-]+)/(?P<repo>[\\w.-]+)/issues/(?P<number>\\d+)$\")\ndef valid_ref(ref: str) -> bool:\n    c = ref.strip()\n    return bool(_ISSUE_REF.match(c) or _ISSUE_URL.match(c))","typeGuard":null,"tryCatchPattern":"try:\n    owner_repo, number = parse_issue_ref(ref)\nexcept InvalidIssueRef as e:\n    return f\"bad issue ref: {e}\"","preventionTips":["Always include owner/repo, never a bare #NN","Convert /pull/NN URLs or reject them before calling","strip() user input before parsing"],"tags":["validation","parsing","argument-error"],"backgroundTag":"invalid-issue-reference","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}