vercel/next.js · critical · syn::Error
serialization = "hash" only makes sense with cell = "compare
Error message
serialization = "hash" only makes sense with cell = "compare" (or default)
What it means
This is a compile-time error emitted by the #[turbo_tasks::value] proc-macro. serialization = "hash" persists only a hash of the value so post-eviction reads can skip re-derivation by comparing hashes — which only works when cells are deduplicated by value comparison (cell = "compare", the default). Combining serialization = "hash" with cell = "keyed" or cell = "new" is contradictory, so the macro refuses to compile.
Source
Thrown at turbopack/crates/turbo-tasks-macros/src/value_macro.rs:282
pub fn value(args: TokenStream, input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as Item);
let ValueArguments {
serialization_mode,
evict_mode,
shared,
cell_mode,
manual_eq,
manual_hash,
transparent,
operation,
task_input,
} = parse_macro_input!(args as ValueArguments);
// `serialization = "hash"` only makes sense with `cell = "compare"` (the default).
if matches!(serialization_mode, SerializationMode::Hash)
&& !matches!(cell_mode, CellMode::Compare)
{
return syn::Error::new(
proc_macro2::Span::call_site(),
"serialization = \"hash\" only makes sense with cell = \"compare\" (or default)",
)
.to_compile_error()
.into();
}
// `hash = "manual"` only makes sense with `serialization = "hash"`.
if manual_hash && !matches!(serialization_mode, SerializationMode::Hash) {
return syn::Error::new(
proc_macro2::Span::call_site(),
"hash = \"manual\" only makes sense with serialization = \"hash\"",
)
.to_compile_error()
.into();
}
// `evict = "last"` only makes sense for `serialization = "skip"`: itView on GitHub (pinned to 0ae8c72462)
Solutions
- Remove the cell override so it defaults to "compare": #[turbo_tasks::value(serialization = "hash")].
- Or explicitly set cell = "compare" if you want it documented.
- If you genuinely need cell = "new" or "keyed", change serialization to "auto", "custom", or "skip" instead of "hash".
Example fix
// before
#[turbo_tasks::value(serialization = "hash", cell = "new")]
struct Foo { ... }
// after
#[turbo_tasks::value(serialization = "hash")] // cell defaults to "compare"
struct Foo { ... } Defensive patterns
Strategy: validation
Prevention
- When using serialization = "hash", omit the cell attribute (defaults to compare) or set cell = "compare" explicitly.
- Use cell = "new"/"keyed" only with serialization modes auto, custom, or skip.
- Cross-check attribute combinations against the macro validation rules in value_macro.rs:278-317.
When it happens
Trigger: Annotating a value type with both serialization = "hash" and a non-compare cell mode, e.g. #[turbo_tasks::value(serialization = "hash", cell = "new")] or cell = "keyed".
Common situations: A Turbopack contributor adding a new value type and copying attributes from another type without understanding the serialization/cell interaction; refactoring a type's cell mode while leaving serialization = "hash".
Related errors
- hash = "manual" only makes sense with serialization = "hash"
- evict = "last" is only valid with serialization = "skip"
- gave up applying effects after {MAX_RETRIES} retries; repeat
AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06).
Data as JSON: /api/errors/c8162676d9fb245b.
Report an issue: GitHub.