vercel/turborepo · critical

failed to calculate Turbo hash: {err}

Error message

failed to calculate Turbo hash: {err}

What it means

turborepo-hash implements a capnproto-based Hashable trait: try_hash serializes the message and xxh64-hashes its first segment, returning an error when the capnp "message is not canonical". The convenience hash() converts any such error into a panic ("failed to calculate Turbo hash"), so this is an internal invariant failure inside turbo's task hashing, not a caller-configurable condition.

Source

Thrown at crates/turborepo-hash/src/traits.rs:30

    fn try_hash(self) -> Result<String, Error> {
        let message = self.into_builder()?;

        debug_assert_eq!(
            message.get_segments_for_output().len(),
            1,
            "message is not canonical"
        );

        let buf = message.get_segments_for_output()[0];

        let out = xxhash_rust::xxh64::xxh64(buf, 0);

        Ok(hex::encode(out.to_be_bytes()))
    }

    fn hash(self) -> String {
        self.try_hash()
            .unwrap_or_else(|err| panic!("failed to calculate Turbo hash: {err}"))
    }
}

View on GitHub (pinned to f9245100cf)

Solutions

  1. Upgrade turborepo — known serialization bugs are fixed in patches
  2. Clear the local turbo cache and .turbo directories, then retry
  3. If it reproduces, minimize the turbo.json/workspace setup and file an issue
  4. In downstream Rust code, call try_hash() instead of hash() to get a Result

Example fix

// before
let hash = item.hash(); // panics on non-canonical message
// after
match item.try_hash() {
    Ok(hash) => { /* use hash */ }
    Err(err) => { /* degrade gracefully: skip caching, log */ }
}
Defensive patterns

Strategy: fallback

Validate before calling

// Prefer the Result-returning API in downstream Rust code
match hashable.try_hash() {
    Ok(h) => use_hash(h),
    Err(e) => {
        tracing::warn!("hash failed, skipping cache: {e}");
        return None;
    }
}

Try / catch

std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| item.hash()))
    .map_err(|p| {
        let msg = p
            .downcast_ref::<&str>()
            .map(|s| s.to_string())
            .unwrap_or_else(|| "hash panic".into());
        msg
    })
    .and_then(|h| Ok(h))

Prevention

When it happens

Trigger: Hashing inputs whose capnp serialization is non-canonical — an internal API misuse or a serialization bug in the caller building the message; effectively unreachable from JavaScript/TypeScript surfaces, it aborts the turbo process.

Common situations: Seen inside `turbo run` after upgrading across serialization changes, with unusual workspace/task configurations, or with corrupted in-memory state; never triggered by config files alone.

Related errors


AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17). Data as JSON: /api/errors/a4462c856deaa537. Report an issue: GitHub.