cross-rs/cross · error

pr should be a number, got

Error message

pr should be a number, got {:?}

What it means

parse_gh_labels shells out to `gh pr view <pr> --json labels` to fetch labels for a pull request. Before invoking the command it validates with eyre::ensure! that the `pr` string consists solely of ASCII digits, because it is passed directly as a CLI argument. Any non-digit character (including an empty string or a full URL) fails with 'pr should be a number, got {pr:?}'.

Solutions

  1. Extract only the numeric portion before calling: strip the URL/'#' prefix and pass just the digits (e.g. refs/pull/123/merge -> "123").
  2. Check the env var or input that supplied the pr value for emptiness or surrounding characters and fix the parsing that produced it.
  3. If a URL is what you have, run `gh pr view <url>` yourself instead, or derive the number with `basename`/regex before invoking the xtask command.

Example fix

// before
let pr = env::var("GITHUB_REF")?; // "refs/pull/123/merge"
apply_ci_labels(&pr)?;

// after
let pr = env::var("GITHUB_REF")?
    .split('/')
    .nth(2)
    .expect("GITHUB_REF to contain pr number")
    .to_string(); // "123"
apply_ci_labels(&pr)?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_pr_number(pr: &str) -> bool { !pr.is_empty() && pr.chars().all(|c| c.is_ascii_digit()) }
assert!(is_pr_number(&pr), "pr must be a numeric id, got {pr:?}");

Type guard

fn parse_pr_number(s: &str) -> Option<u64> { s.parse::<u64>().ok() }

Try / catch

match parse_pr_number(&pr) {
    Some(n) => apply_ci_labels(&n.to_string())?,
    None => eyre::bail!("GITHUB_REF/pr input is not a numeric PR id: {pr:?}"),
}

Prevention

When it happens

Trigger: Calling apply_ci_labels / has_no_ci_target_label / has_no_ci_tests_label with a `pr` argument that is not a plain numeric ID: e.g. passing a PR URL ("https://github.com/org/repo/pull/123"), a value with a '#' prefix ("#123"), whitespace, or an empty string pulled from a mis-parsed GITHUB_REF or CI event payload.

Common situations: Extracting the PR number from GITHUB_REF ('refs/pull/123/merge') with a faulty split; a developer passing a branch name or URL manually; an environment variable that is unset or contains stray characters being fed as the pr argument in CI scripts.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of cross-rs/cross@8c1a8aa4b6 (2026-09-13). Data as JSON: /api/errors/6b29aa8407be2027. Report an issue: GitHub.

Appendix: source

Thrown at xtask/src/ci/target_matrix.rs:186

            Ok::<_, eyre::Report>(b && has_no_ci_target_label(pr)?)
        })?
    {
        app.none = true;
    }
    Ok(())
}

fn parse_gh_labels(pr: &str) -> cross::Result<Vec<String>> {
    #[derive(Deserialize)]
    struct PullRequest {
        labels: Vec<PullRequestLabels>,
    }

    #[derive(Deserialize)]
    struct PullRequestLabels {
        name: String,
    }
    eyre::ensure!(
        pr.chars().all(|c| c.is_ascii_digit()),
        "pr should be a number, got {:?}",
        pr
    );
    let stdout = Command::new("gh")
        .args(["pr", "view", pr, "--json", "labels"])
        .run_and_get_stdout(&mut Verbosity::Quiet.into())?;
    let pr_info: PullRequest = serde_json::from_str(&stdout)?;
    Ok(pr_info.labels.into_iter().map(|l| l.name).collect())
}

fn has_no_ci_target_label(pr: &str) -> cross::Result<bool> {
    Ok(parse_gh_labels(pr)?.contains(&"no-ci-targets".to_owned()))
}

fn has_no_ci_tests_label(pr: &str) -> cross::Result<bool> {
    Ok(parse_gh_labels(pr)?.contains(&"no-ci-tests".to_owned()))
}

View on GitHub (pinned to 8c1a8aa4b6)