clockworklabs/SpacetimeDB · error · syn::Error

unions not supported

Error message

unions not supported

What it means

The SpacetimeDB derive macros can only generate SATS schema for Rust structs (product types) and enums (sum types); a `union` has no representation in the SATS type system. When a derive such as `SpacetimeType` or `#[spacetimedb::table(...)]` is applied to a `union` item, `sats_type_from_derive` rejects it at the union token span. Unions are also unsafe to read in general, so no serialization code can be generated for them.

Source

Thrown at crates/bindings-macro/src/sats.rs:82

                ty: &field.ty,
                original_attrs: &field.attrs,
            });
            SatsTypeData::Product(fields.collect())
        }
        syn::Data::Enum(enu) => {
            let variants = enu.variants.iter().map(|var| {
                let (member, ty) = variant_data(var)?.unzip();
                Ok(SatsVariant {
                    ident: &var.ident,
                    name: var.ident.to_string(),
                    ty,
                    member,
                    original_attrs: &var.attrs,
                })
            });
            SatsTypeData::Sum(variants.collect::<syn::Result<Vec<_>>>()?)
        }
        syn::Data::Union(u) => return Err(syn::Error::new(u.union_token.span, "unions not supported")),
    };
    extract_sats_type(&input.ident, &input.generics, &input.attrs, data, crate_fallback)
}

fn is_repr_c(attrs: &[syn::Attribute]) -> bool {
    let mut is_repr_c = false;
    for attr in attrs.iter().filter(|a| a.path() == sym::repr) {
        let _ = attr.parse_nested_meta(|meta| {
            is_repr_c |= meta.path.is_ident("C");
            Ok(())
        });
    }
    is_repr_c
}

pub(crate) fn extract_sats_type<'a>(
    ident: &'a syn::Ident,
    generics: &'a syn::Generics,

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Replace the union with an enum — enums map to SATS sum types and are fully supported
  2. If the union encoded tagged data, convert it to a struct holding an enum field
  3. Keep unions out of schema types entirely: convert or wrap them before storing in tables

Example fix

// before
#[derive(spacetimedb::SpacetimeType)]
union Value {
    int: i64,
    text: String,
}

// after
#[derive(spacetimedb::SpacetimeType)]
enum Value {
    Int(i64),
    Text(String),
}
Defensive patterns

Strategy: validation

Validate before calling

// tests/ui.rs — encode unsupported constructs as expected compile failures in CI
#[test]
fn ui() {
    let t = trybuild::TestCases::new();
    t.compile_fail("tests/ui/union_sats_type.rs"); // expects "unions not supported"
}

Prevention

When it happens

Trigger: Applying `#[derive(SpacetimeType)]` or `#[spacetimedb::table(...)]` to a Rust `union` item; any derive path that feeds a `syn::Data::Union` into `sats_type_from_derive`.

Common situations: Porting C-style FFI code that used `#[repr(C)] union` for zero-cost variants into a SpacetimeDB module; reusing an existing domain type as a table or column type without reshaping it first.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/40053b9610527795. Report an issue: GitHub.