jdx/mise · error · eyre::Report

cache agent returned an incomplete blob lookup response

Error message

cache agent returned an incomplete blob lookup response

What it means

A FindBlobs request was answered with AgentResponse::Blobs whose paths vector length differs from the number of digests requested (src/cache/rustc.rs:499-501). The protocol requires exactly one path slot per digest, so this is a response-shape violation by the agent, not a cache-state problem. It points at protocol/version skew between the shim and the agent process behind MISE_CACHE_SOCKET.

Source

Thrown at src/cache/rustc.rs:500

fn find_blobs(digests: &[CacheDigest]) -> Result<Vec<PathBuf>> {
    let responses = session::request_agent(&[AgentRequest::FindBlobs {
        digests: digests.to_vec(),
    }])?;
    let Some(response) = responses.into_iter().next() else {
        bail!("cache agent did not return a blob lookup response");
    };
    match response {
        AgentResponse::Blobs { paths } if paths.len() == digests.len() => paths
            .into_iter()
            .zip(digests)
            .map(|(path, digest)| match path {
                Some(path) => Ok(path),
                None => bail!("cached rustc action is missing blob {}", digest.hash),
            })
            .collect(),
        AgentResponse::Blobs { .. } => {
            bail!("cache agent returned an incomplete blob lookup response")
        }
        AgentResponse::Blob { path: Some(path) } if digests.len() == 1 => Ok(vec![path]),
        AgentResponse::Blob { path: None } if digests.len() == 1 => {
            let digest = &digests[0];
            bail!("cached rustc action is missing blob {}", digest.hash)
        }
        AgentResponse::Error { message } => bail!(message),
        _ => bail!("cache agent returned an unexpected blob lookup response"),
    }
}

fn read_canonical_blob<T>(path: &Path, digest: &CacheDigest, description: &str) -> Result<T>
where
    T: DeserializeOwned + Serialize,
{
    let bytes = read_verified_blob(path, digest, description)?;
    let value = serde_json::from_slice(&bytes)
        .wrap_err_with(|| format!("cached {description} is not valid JSON"))?;

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Unset inherited MISE_CACHE_SOCKET, MISE_CACHE_STAGING_DIR, MISE_CACHE_TASK and MISE_CACHE_TASK_ROOT in your shell/CI profile and let mise inject them per task
  2. Restart the mise task/session so the shim and agent come from the same mise binary (the handshake enforces agent_version == VERSION, src/cache/session.rs:821-830)
  3. Confirm a single mise version is active inside and outside the task (mise --version in both contexts)
  4. If it persists with matched versions, report it as a bug with both versions and the request digest count
Defensive patterns

Strategy: validation

Validate before calling

# in shell profiles / CI images: never inherit a previous session's agent socket
unset MISE_CACHE_SOCKET MISE_CACHE_STAGING_DIR MISE_CACHE_TASK MISE_CACHE_TASK_ROOT MISE_CACHE_CARGO_TARGET_DIR
# mise re-injects these per task it launches

Type guard

fn is_incomplete_blobs(r: &AgentResponse, n: usize) -> bool {
    matches!(r, AgentResponse::Blobs { paths } if paths.len() != n)
}

Prevention

When it happens

Trigger: session::request_agent returns Blobs with paths.len() != digests.len(): caused by a version-skewed agent process (MISE_CACHE_SOCKET pointing at a session spawned by a different mise build whose AgentResponse enum differs), a stale socket inherited from a previous session, or a deserialized/garbled response line.

Common situations: Exporting MISE_CACHE_SOCKET/MISE_CACHE_* in a shell profile or CI base image so tasks attach to an old agent; running two mise versions side by side where one reuses the other's session directory; a hand-crafted client speaking a different protocol revision to the socket.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/fe2d281621372144. Report an issue: GitHub.