denoland/deno · error · syn::Error

FromV8 enum derive currently supports only unit and single-e

Error message

FromV8 enum derive currently supports only unit and single-element newtype variants

What it means

#[derive(FromV8)] on an enum currently implements only the externally-tagged shape: unit variants (matched by their camelCased tag string) and single-element newtype variants (matched when the key exists and is not undefined). Any struct variant (V { a: u32 }) or tuple variant with zero or 2+ fields falls into the catch-all arm and produces this compile-time error on the offending variant's span.

Source

Thrown at libs/ops/conversion/from_v8/enum.rs:82

          }
        };
        let key = crate::get_internalized_string(Ident::new(
          &tag_name,
          variant_ident.span(),
        ))?;
        variant_arms.push(quote! {
          {
            let __key = #key;
            if let Some(__inner) = __obj.get(__scope, __key)
              && !__inner.is_undefined()
            {
              return Ok(Self::#variant_ident(#converter));
            }
          }
        });
      }
      _ => {
        return Err(Error::new(
          variant_span,
          "FromV8 enum derive currently supports only unit and single-element newtype variants",
        ));
      }
    }
  }

  let unit_branch = if unit_arms.is_empty() {
    quote! {}
  } else {
    quote! {
      if let Ok(__s) =
        ::deno_core::v8::Local::<::deno_core::v8::String>::try_from(__value)
      {
        let __s = __s.to_rust_string_lossy(__scope);
        match __s.as_str() {
          #(#unit_arms)*
          _ => {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Convert each struct/multi-field variant into a newtype variant wrapping a struct: move the fields into their own #[derive(FromV8)] struct and declare the variant as Point(Point).
  2. Keep single payloads as one-field newtype variants (V(u32)) — those and unit variants are the only supported shapes.
  3. For complex payloads, use the serde path on the newtype variant so serde_v8::from_v8 handles the inner value instead of the derive.

Example fix

// before
#[derive(FromV8)]
enum Shape {
  Circle,
  Point { x: f64, y: f64 }, // error: only unit and single-element newtype variants
}

// after
#[derive(FromV8)]
struct Point {
  x: f64,
  y: f64,
}

#[derive(FromV8)]
enum Shape {
  Circle,
  Point(Point), // newtype variant wrapping a derived struct
}
Defensive patterns

Strategy: validation

Type guard

// design-time shape check: FromV8-compatible enum variants are unit or single-newtype only
// variant is OK:   Unit            | Newtype(OneType)
// variant is NOT:  Struct { a: A } | Tuple(A, B) | Empty()
// when reviewing an enum before adding #[derive(FromV8)]:
//   every variant must match `Ident` or `Ident(Ty)` with exactly one Ty

Prevention

When it happens

Trigger: Deriving FromV8 on an enum that contains a struct variant like Point { x: f64, y: f64 } or a multi-field tuple variant like Pair(u32, u32); also a variant declared with empty parens V().

Common situations: Porting serde-tagged enums into deno_core ops and expecting the same flexibility; adding a data-carrying variant to a previously unit-only op enum; mixing #[to_v8] (which does support struct variants) with #[from_v8] and assuming symmetric support.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/d065df6ed2c80a44. Report an issue: GitHub.