Morganamilo/paru · info

package does not have an update

Error message

package does not have an update

What it means

src/devel.rs:490 (has_update) compares the locally recorded VCS commit with the latest commit resolved from the remote. If they are equal — or the caller reaches this point with no divergence — the function bails with "package does not have an update". It is a control-flow signal (bail used as a sentinel), not a malfunction: the devel package is already at the newest commit.

Solutions

  1. Treat this message as 'up to date' — match on the error string or refactor the API to return Ok(false)/Option instead of bailing.
  2. No action is needed for the package itself; it already has the latest commit.
  3. If you expected an update, verify the cached devel info (url.commit) is fresh — delete the stored devel info cache so it is re-fetched.
  4. Confirm git ls-remote returned the real remote HEAD (see error 17/18 paths) and not a stale/cached value.

Example fix

// before: treating any error as failure
match devel::has_update(&pkg).await {
    Ok(_) => update(),
    Err(e) => return Err(e),
}

// after: treat "no update" sentinel as up-to-date
match devel::has_update(&pkg).await {
    Ok(_) => update(),
    Err(e) if e.to_string().contains("package does not have an update") => { /* up to date */ }
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Compare commits yourself before calling, to anticipate the no-update bail
let up_to_date = stored_devel_info
    .get(&pkg.name)
    .map(|info| info.commit == latest_remote_sha)
    .unwrap_or(true);

Try / catch

match devel::has_update(&pkg).await {
    Ok(_) => update(pkg),
    Err(e) if e.to_string().contains("package does not have an update") => {
        // expected sentinel: package is current, not a failure
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling has_update for a VCS package whose fetched remote SHA equals the stored sha (url.commit == sha). Any caller that treats every Result from has_update as success will see this as an error even though it means 'nothing to do'.

Common situations: Running an update sweep where all -git packages are already current; re-running a devel check shortly after a previous update; a caller that forgets this specific bail is the expected no-update outcome.

Related errors


AI-assisted analysis of Morganamilo/paru@9ac3578807 (2026-09-12). Data as JSON: /api/errors/c0fd3b8a53e558a0. Report an issue: GitHub.

Appendix: source

Thrown at src/devel.rs:490

    } else {
        None
    }
}

async fn has_update(style: Style, git: &str, flags: &[String], url: &RepoInfo) -> Result<()> {
    let sha = ls_remote(style, git, flags, url.url.clone(), url.branch.as_deref()).await?;
    debug!(
        "devel check {}: '{}' == '{}' different: {}",
        url.url,
        url.commit,
        sha,
        url.commit != sha
    );
    if sha != *url.commit {
        return Ok(());
    }

    bail!(tr!("package does not have an update"))
}

pub async fn fetch_devel_info(
    config: &Config,
    bases: &[Base],
    srcinfos: &HashMap<String, Srcinfo>,
) -> Result<DevelInfo> {
    let mut devel_info = DevelInfo::default();

    let mut parsed = Vec::new();
    let mut futures = Vec::new();

    for base in bases {
        let srcinfo = match base {
            Base::Aur(_) => srcinfos.get(base.package_base()),
            Base::Pkgbuild(c) => Some(c.srcinfo.as_ref()),
        };

View on GitHub (pinned to 9ac3578807)