{"record":{"id":"627385b0e3a30b07","repo":"dbt-labs/dbt-core","slug":"unnamed-field-not-supported","errorCode":null,"errorMessage":"Unnamed field not supported","messagePattern":"Unnamed field not supported","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"crates/proto-rust-macros/src/lib.rs","lineNumber":69,"sourceCode":"        },\n        _ => {\n            return Err(syn::Error::new(\n                input.span(),\n                \"ProtoNew can only be derived for structs\",\n            ));\n        }\n    };\n\n    // Build parameter list and field initializers\n    let mut params = Vec::new();\n    let mut inits = Vec::new();\n    let mut arg_docs: Vec<String> = Vec::new();\n\n    for field in fields {\n        let field_ident = field\n            .ident\n            .clone()\n            .ok_or_else(|| syn::Error::new(field.span(), \"Unnamed field not supported\"))?;\n\n        let is_enum = find_prost_enumeration_attr(&field.attrs)?;\n\n        let param_ident = field_ident.clone();\n\n        // Collect doc for this field\n        if let Some(doc) = extract_field_doc(&field.attrs) {\n            let name = field_ident.to_string();\n            let arg_doc = format!(\"* `{name}` - {doc}\");\n            arg_docs.push(arg_doc);\n        }\n\n        if let Some(enum_path) = is_enum {\n            // Replace i32 in the outer shape with enum path for the parameter type,\n            // and create an initializer that converts enum(s) back to i32.\n            let (param_ty_tokens, init_expr) =\n                map_enum_param_and_init(&field.ty, &enum_path, &param_ident)?;\n            params.push(quote! { #param_ident: #param_ty_tokens });","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/proto-rust-macros/src/lib.rs#L51-L87","documentation":"The ProtoNew derive macro generates a `new()` constructor from a struct's fields, and it requires every field to be named so it can emit a matching named parameter. It is a compile-time (proc-macro) error raised when the derived struct contains tuple-style or unit fields, which have no identifier to turn into a constructor argument.","triggerScenarios":"Applying #[derive(ProtoNew)] to a struct declared with unnamed fields (e.g. `struct Msg(i32);`) or unit variants/fields (`struct Msg;`) instead of named fields (`struct Msg { id: i32 }`).","commonSituations":"Hand-written prost message structs that mimic tuple structs; copied Rust code (like newtype wrappers) that happens to carry the derive; code generated from a .proto with unusual shapes then hand-edited into tuple form.","solutions":["Convert the struct to use named fields: `struct Msg(i32);` -> `struct Msg { value: i32 }`.","If the type is a genuine newtype wrapper, remove `#[derive(ProtoNew)]` and implement `new()` manually.","Keep prost-generated message structs untouched and derive ProtoNew only on structs produced from .proto definitions, which always have named fields."],"exampleFix":"// before\n#[derive(ProtoNew)]\nstruct MyMessage(i32);\n\n// after\n#[derive(ProtoNew)]\nstruct MyMessage {\n    value: i32,\n}","handlingStrategy":"validation","validationCode":"// compile-time check: derive fails fast if any field is unnamed\nfn assert_named_fields<T>() {}\n// Ensure struct shape before deriving:\n// struct Msg { id: i32 }  // all fields must be `name: Type`","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only apply ProtoNew to structs with all-named fields","Never hand-write tuple structs for prost messages","Let code review/lint flag derive attributes on newtype wrappers"],"tags":["proc-macro","derive-macro","compile-time","rust","protobuf"],"backgroundTag":"unsupported-operation","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}