jdx/mise · error

remote action result is missing its output root

Error message

remote action result is missing its output root

What it means

A downloaded remote action result must include an output-root tree when its manifest lists roots (files to restore). If output_root is absent while roots is non-empty, the entry is internally inconsistent — metadata promises artifacts that were never uploaded — and mise refuses to fabricate them.

Source

Thrown at src/task/task_cache_store.rs:418

        let metadata_bytes = self
            .client
            .get_blob(metadata, CLIENT_METADATA_MEDIA_TYPE)
            .await?;
        let metadata: RemoteClientMetadata = serde_json::from_slice(&metadata_bytes)?;
        if canonical_json(&serde_json::to_value(&metadata)?)? != metadata_bytes {
            bail!("remote cache client metadata is not canonical JSON");
        }
        let mut manifest = metadata.into_manifest(key)?;
        for root in &manifest.roots {
            validate_cache_path(root)?;
        }
        let artifact = match &result.output_root {
            Some(root) => {
                let temporary = materialize_remote_tree(self, root).await?;
                Some(TaskCacheStoreArtifact::temporary(temporary))
            }
            None if manifest.roots.is_empty() => None,
            None => bail!("remote action result is missing its output root"),
        };
        manifest.artifact_checksum = Some(calculate_artifact_checksum(
            &manifest,
            artifact.as_ref().map(TaskCacheStoreArtifact::path),
        )?);
        let manifest = serde_json::to_vec(&manifest)?;
        Ok(Some(TaskCacheStoreEntry { manifest, artifact }))
    }

    fn begin_write(&self, key: &str) -> Result<TaskCacheStoreWrite> {
        validate_remote_key(key)?;
        file::create_dir_all(&self.staging_dir)?;
        let nonce = crate::rand::random_string(8);
        Ok(TaskCacheStoreWrite {
            artifact_path: self.staging_dir.join(format!("{key}.part-{nonce}.tar.zst")),
            manifest_path: self.staging_dir.join(format!("{key}.part-{nonce}.json")),
        })
    }

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Purge the offending remote key (or its prefix) so the task re-runs and writes a complete entry
  2. If it recurs, look for interrupted uploads: CI timeouts, proxy body-size limits, flaky networks
  3. Report upstream if entries are consistently half-written on a current pinned mise

Example fix

# before: half-uploaded entry keeps failing restore
# after: evict and re-run
aws s3 rm --recursive s3://cache/tasks/<key-prefix>
mise run build
Defensive patterns

Strategy: fallback

Try / catch

// a half-written remote entry is a miss; evict and rebuild
match fetch_remote_entry(&key).await {
    Ok(e) => Some(e),
    Err(err) if err.to_string().contains("remote action result is missing its output root") => {
        evict_remote(&key).await;
        None
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: get() matches result.output_root = None with a manifest whose roots are non-empty — typically a commit interrupted after metadata upload but before the artifact tree, leaving a half-written entry.

Common situations: Upload killed mid-transaction (network drop, CI cancel, timeout); a writer bug in an experimental build; lifecycle rules deleting blobs but not metadata.

Related errors


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