cross-rs/cross · info

should contain one item

Error message

should contain one item

What it means

This `expect` fires inside PreBuild::Single construction in `pre_build` (src/config.rs:151). After `split('\n')` and a `v.len() == 1` check, the code re-collects into a Vec and calls `next().expect("should contain one item")`. The expect documents the invariant that a single-element iterator always yields one item; it can only fail if that invariant is broken, which is logically unreachable with a correctly-checked len==1.

Solutions

  1. Treat this as an internal invariant, not a config error; if it ever fires, report it as a bug in cross
  2. If refactoring, avoid the re-collect: use `v.into_iter().next().unwrap()` immediately after the len check, or match on the slice `if let [line] = &v[..]`
  3. For config problems, check the PRE_BUILD value in Cross.toml instead — single-line vs multi-line is decided by the len==1 branch, not this expect

Example fix

// before
if v.len() == 1 {
    PreBuild::Single { line: v.into_iter().next().expect("should contain one item"), env: true }
}
// after
if let [line] = v.as_slice() {
    PreBuild::Single { line: line.clone(), env: true }
}
Defensive patterns

Strategy: validation

Validate before calling

let lines: Vec<&str> = pre_build_value.split('\n').collect();
if lines.len() != 1 { /* it will take the PreBuild::Lines branch; no panic path */ }

Type guard

fn is_single_line(v: &str) -> bool { !v.contains('\n') }

Prevention

When it happens

Trigger: Only reachable if the `v.len() == 1` guard is bypassed or the Vec is emptied between the check and the `next()` call. In practice this never fires for users; it is a defensive assertion. A PRE_BUILD config value with no newline (single line) takes this branch.

Common situations: Developers essentially never hit this at runtime; it surfaces as a panic in code review or fuzzing if the guard were ever removed. Confusion usually comes from reading the expect message and thinking a config file was malformed.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/config.rs:151

    fn image(&self, target: &Target) -> Result<Option<PossibleImage>> {
        let get_target = |env: &Environment, var: &str| env.get_target_var(target, var);
        get_possible_image(self, "IMAGE", "IMAGE_TOOLCHAIN", get_target, get_target)
    }

    fn dockerfile(&self, target: &Target) -> ConfVal<String> {
        self.get_values_for("DOCKERFILE", target, ToOwned::to_owned)
    }

    fn dockerfile_context(&self, target: &Target) -> ConfVal<String> {
        self.get_values_for("DOCKERFILE_CONTEXT", target, ToOwned::to_owned)
    }

    fn pre_build(&self, target: &Target) -> ConfVal<PreBuild> {
        self.get_values_for("PRE_BUILD", target, |v| {
            let v: Vec<_> = v.split('\n').map(String::from).collect();
            if v.len() == 1 {
                PreBuild::Single {
                    line: v.into_iter().next().expect("should contain one item"),
                    env: true,
                }
            } else {
                PreBuild::Lines(v)
            }
        })
    }

    fn runner(&self, target: &Target) -> Option<String> {
        self.get_target_var(target, "RUNNER")
    }

    fn passthrough(&self, target: &Target) -> ConfVal<Vec<String>> {
        self.get_values_for("ENV_PASSTHROUGH", target, split_to_cloned_by_ws)
    }

    fn volumes(&self, target: &Target) -> ConfVal<Vec<String>> {
        self.get_values_for("ENV_VOLUMES", target, split_to_cloned_by_ws)

View on GitHub (pinned to 8c1a8aa4b6)