can1357/oh-my-pi · error · ManualTriageError

{repo_full}#{number} is a pull request, not an issue

Error message

{repo_full}#{number} is a pull request, not an issue

What it means

ManualTriageError raised by build_issues_opened_payload when the fetched GitHub item has the is_pull_request flag set, meaning manual triage was pointed at a PR instead of a real issue. It synthesizes `issues.opened` payloads only for issues.

Source

Thrown at python/robomp/src/manual_triage.py:69

def parse_issue_ref(ref: str) -> tuple[str, int]:
    """Parse `owner/repo#NN` or a github issue url into `("owner/repo", NN)`."""
    cleaned = ref.strip()
    match = _ISSUE_REF.match(cleaned) or _ISSUE_URL.match(cleaned)
    if match is None:
        raise InvalidIssueRef(f"expected owner/repo#NN or https://github.com/owner/repo/issues/NN, got {ref!r}")
    return f"{match.group('owner')}/{match.group('repo')}", int(match.group("number"))


def manual_delivery_id(repo_full: str, number: int) -> str:
    """Stable delivery id for manually-triggered triage. Re-runs reuse it."""
    return f"manual-{repo_full.replace('/', '__')}-{number}"


async def build_issues_opened_payload(github: GitHubBackend, repo_full: str, number: int) -> dict[str, Any]:
    """Fetch the issue + repo metadata and synthesize an `issues.opened` payload."""
    issue = await github.get_issue(repo_full, number)
    if issue.is_pull_request:
        raise ManualTriageError(f"{repo_full}#{number} is a pull request, not an issue")
    repo = await github.get_repo(repo_full)
    return {
        "action": "opened",
        "issue": {
            "number": issue.number,
            "title": issue.title,
            "body": issue.body,
            "state": issue.state,
            "user": {"login": issue.author},
            "labels": [{"name": lbl} for lbl in issue.labels],
        },
        "repository": {
            "full_name": repo.full_name,
            "default_branch": repo.default_branch,
            "clone_url": repo.clone_url,
            "private": repo.private,
        },
    }

View on GitHub (pinned to 9690622007)

Solutions

  1. Use a genuine issue number, not a PR number
  2. Check the item first (e.g. via the GitHub web UI) — URLs ending in /pull/NN are PRs
  3. If PR triage is needed, use the PR-specific flow instead of manual issue triage

Example fix

// before
await enqueue_manual_triage(github, db, "org/repo", 42)  # 42 is a PR
// after: pick the issue number
await enqueue_manual_triage(github, db, "org/repo", 43)  # real issue
Defensive patterns

Strategy: validation

Validate before calling

issue = await github.get_issue(repo_full, number)
if issue.is_pull_request:
    raise ValueError(f"{repo_full}#{number} is a PR, not an issue")

Type guard

def is_real_issue(issue) -> bool:
    return not issue.is_pull_request

Try / catch

try:
    delivery = await enqueue_manual_triage(github, db, repo_full, number)
except ManualTriageError as e:
    log.info("not an issue: %s", e)
    return None

Prevention

When it happens

Trigger: enqueue_manual_triage(repo, number) where number resolves to a PR; GitHub's issues API returns PRs under the /issues endpoint so numbers overlap.

Common situations: Copy-pasting a PR number/URL into manual triage, off-by-one confusion since issue and PR numbers share one sequence per repo.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/4be8b3f222d58831. Report an issue: GitHub.