jdx/mise · error · eyre::Report

cache agent returned a non-canonical rustc action prediction

Error message

cache agent returned a non-canonical rustc action prediction

What it means

A prediction payload is JSON that must be in canonical form: the wrapper re-serializes the parsed RustcInputPrediction with canonical_json and requires byte-identical output to the payload received. Any difference (key ordering, whitespace, number formatting) means the agent stored or returned non-canonical JSON, so downstream digests could diverge, and the wrapper bails (src/cache/rustc.rs:149).

Source

Thrown at src/cache/rustc.rs:149

        invocation: invocation_digest.clone(),
    }])?;
    let Some(response) = responses.into_iter().next() else {
        bail!("cache agent did not return an action prediction response");
    };
    let prediction = match response {
        AgentResponse::ActionPrediction {
            prediction: Some(prediction),
        } => prediction,
        AgentResponse::ActionPrediction { prediction: None } => return Ok(None),
        AgentResponse::Error { message } => bail!(message),
        _ => bail!("cache agent returned an unexpected action prediction response"),
    };
    if prediction.adapter != "rustc" || prediction.invocation != invocation_digest {
        bail!("cache agent returned an incompatible rustc action prediction");
    }
    let input_prediction: RustcInputPrediction = serde_json::from_str(&prediction.payload)?;
    if String::from_utf8(canonical_json(&input_prediction)?)? != prediction.payload {
        bail!("cache agent returned a non-canonical rustc action prediction");
    }
    let discovered = input_prediction.discover(working_dir, &context.path_mappings)?;
    discovered.clone().apply_to(&mut context)?;
    let action = invocation.action(context)?;
    let restored = restore_result(&action, outputs, &discovered, restore_outputs)?;
    if restored.is_some() {
        record_prediction_value(invocation_digest, action.digest.clone(), prediction.payload);
    }
    Ok(restored)
}

fn action_from_dep_info(
    rustc: &OsStr,
    invocation: &RustcInvocation,
    dep_info: &Path,
    working_dir: &Path,
) -> Result<(RustcAction, DiscoveredInputs)> {
    let dep_info = RustcDepInfo::read(dep_info)?;

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Restart the agent and rebuild with the current mise so payloads are re-recorded canonically
  2. Delete the agent-side cache blobs so old payloads are regenerated
  3. Keep agent and wrapper on the same mise build; disable the rustc cache if you must mix versions
Defensive patterns

Strategy: retry

Try / catch

# Canonicalization failures mean stale payloads: reset cache and retry
grep -q 'non-canonical rustc action prediction' build.log && {
  rm -rf "$MISE_CACHE_STAGING_DIR"; cargo build;
}

Prevention

When it happens

Trigger: An agent written by (or serialized by) a different mise version whose canonicalization differs — e.g. serde struct field order changed, float formatting changed — making the round-trip comparison fail.

Common situations: Upgrading mise while keeping a long-lived agent/cache; mixing release and development builds against the same cache store.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/58cdc3d485b8707e. Report an issue: GitHub.