{"record":{"id":"d4078636bbe8c5dd","repo":"clockworklabs/SpacetimeDB","slug":"spacetimedb-table-must-be-a-struct","errorCode":null,"errorMessage":"spacetimedb table must be a struct","messagePattern":"spacetimedb table must be a struct","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"crates/bindings-macro/src/table.rs","lineNumber":838,"sourceCode":"    let mut set = GENERATED_STRUCTS.lock().expect(\"mutex poisoned\");\n\n    set.insert(struct_name.to_string())\n}\n\npub(crate) fn table_impl(mut args: TableArgs, item: &syn::DeriveInput) -> syn::Result<TokenStream> {\n    let vis = &item.vis;\n    let sats_ty = sats::sats_type_from_derive(item, quote!(spacetimedb::spacetimedb_lib))?;\n\n    let original_struct_ident = sats_ty.ident;\n    let table_ident = &args.accessor;\n    let explicit_table_name = args.name.as_ref().map(|s| s.value());\n    let view_trait_ident = format_ident!(\"{}__view\", table_ident);\n    let query_trait_ident = format_ident!(\"{}__query\", table_ident);\n    let query_cols_struct = format_ident!(\"{}Cols\", original_struct_ident);\n    let query_ix_cols_struct = format_ident!(\"{}IxCols\", original_struct_ident);\n    let table_name = table_ident.unraw().to_string();\n    let sats::SatsTypeData::Product(fields) = &sats_ty.data else {\n        return Err(syn::Error::new(Span::call_site(), \"spacetimedb table must be a struct\"));\n    };\n\n    for param in &item.generics.params {\n        let err = |msg| syn::Error::new_spanned(param, msg);\n        match param {\n            syn::GenericParam::Lifetime(_) => {}\n            syn::GenericParam::Type(_) => return Err(err(\"type parameters are not allowed on tables\")),\n            syn::GenericParam::Const(_) => return Err(err(\"const parameters are not allowed on tables\")),\n        }\n    }\n\n    let table_id_from_name_func = quote! {\n        fn table_id() -> spacetimedb::TableId {\n            static TABLE_ID: std::sync::OnceLock<spacetimedb::TableId> = std::sync::OnceLock::new();\n            *TABLE_ID.get_or_init(|| {\n                spacetimedb::table_id_from_name(<Self as spacetimedb::table::TableInternal>::TABLE_NAME)\n            })\n        }","sourceCodeStart":820,"sourceCodeEnd":856,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/bindings-macro/src/table.rs#L820-L856","documentation":"`#[spacetimedb::table]` derives the item's SATS type and matches on `SatsTypeData::Product`; only a struct yields Product data. Applying the macro to an enum fails this check and reports that a table must be a struct, at call site. (A union fails even earlier, in sats.rs, with 'unions not supported'.) Value types such as enums belong on columns via `SpacetimeType`, not on tables.","triggerScenarios":"`#[spacetimedb::table(...)]` applied to an `enum` item.","commonSituations":"Trying to use an enum as a lookup table; new users applying the table attribute to every schema type they define.","solutions":["Change the table to a struct","If it is a value type, derive `SpacetimeType` on the enum and use it as a column in a real table struct","Model enum-keyed rows as a struct with an enum-typed column plus an index"],"exampleFix":"// before\n#[spacetimedb::table(accessor = status)]\nenum Status {\n    Active,\n    Banned,\n}\n\n// after\n#[derive(spacetimedb::SpacetimeType)]\nenum Status {\n    Active,\n    Banned,\n}\n\n#[spacetimedb::table(accessor = user)]\nstruct User {\n    #[primary_key]\n    #[auto_inc]\n    id: u64,\n    status: Status,\n}","handlingStrategy":"validation","validationCode":"# tables must be structs: flag non-struct items carrying the table attribute\ngrep -rn -A1 'spacetimedb::table' src/ | grep -E 'enum|union' || echo OK","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Tables are structs; value types are enums with #[derive(SpacetimeType)] used as columns","Keep schema declarations in one reviewable module section","CI cargo check surfaces these before publish"],"tags":["rust","proc-macro","spacetimedb","table","enum","compile-time"],"backgroundTag":"unsupported-type-declaration","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}