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
- Extract only the numeric portion before calling: strip the URL/'#' prefix and pass just the digits (e.g. refs/pull/123/merge -> "123").
- Check the env var or input that supplied the pr value for emptiness or surrounding characters and fix the parsing that produced it.
- 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
- Parse the numeric PR id out of GITHUB_REF with split('/') rather than passing the raw value.
- Strip '#' and URL prefixes at the input boundary of your CI script.
- Validate inputs with eyre::ensure! at the script/entry level before invoking xtask.
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
- Refusing to push without tag or branch. Specify a…
- unexpected progress type: expected plain, auto, or tty and…
- failed to get rustup version
- argument for --color must be auto, always, or never, but…
- no valid choice to pick for image name
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)