{"record":{"id":"6bd0478637577aa9","repo":"hashicorp/consul","slug":"unable-to-convert-configentry-of-kind-s-to-struct","errorCode":null,"errorMessage":"unable to convert ConfigEntry of kind %s to structs","messagePattern":"unable to convert ConfigEntry of kind (.+?) to structs","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proto/private/pbconfigentry/config_entry.go","lineNumber":137,"sourceCode":"\t\treturn &target\n\tcase Kind_KindJWTProvider:\n\t\tvar target structs.JWTProviderConfigEntry\n\t\ttarget.Name = s.Name\n\n\t\tJWTProviderToStructs(s.GetJWTProvider(), &target)\n\t\tpbcommon.RaftIndexToStructs(s.RaftIndex, &target.RaftIndex)\n\t\tpbcommon.EnterpriseMetaToStructs(s.EnterpriseMeta, &target.EnterpriseMeta)\n\t\treturn &target\n\tcase Kind_KindExportedServices:\n\t\tvar target structs.ExportedServicesConfigEntry\n\t\ttarget.Name = s.Name\n\n\t\tExportedServicesToStructs(s.GetExportedServices(), &target)\n\t\tpbcommon.RaftIndexToStructs(s.RaftIndex, &target.RaftIndex)\n\t\tpbcommon.EnterpriseMetaToStructs(s.EnterpriseMeta, &target.EnterpriseMeta)\n\t\treturn &target\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unable to convert ConfigEntry of kind %s to structs\", s.Kind))\n\t}\n}\n\nfunc ConfigEntryFromStructs(s structs.ConfigEntry) *ConfigEntry {\n\tconfigEntry := &ConfigEntry{\n\t\tName:           s.GetName(),\n\t\tEnterpriseMeta: pbcommon.NewEnterpriseMetaFromStructs(*s.GetEnterpriseMeta()),\n\t}\n\n\tvar raftIndex pbcommon.RaftIndex\n\tpbcommon.RaftIndexFromStructs(s.GetRaftIndex(), &raftIndex)\n\tconfigEntry.RaftIndex = &raftIndex\n\n\tswitch v := s.(type) {\n\tcase *structs.MeshConfigEntry:\n\t\tvar meshConfig MeshConfig\n\t\tMeshConfigFromStructs(v, &meshConfig)\n","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/hashicorp/consul/blob/2397ff0d763d34f2fe37fe59fde6a7f7fc430a3e/proto/private/pbconfigentry/config_entry.go#L119-L155","documentation":"ConfigEntryToStructs (proto/private/pbconfigentry/config_entry.go) panics when the proto ConfigEntry's Kind does not match one of the handled cases (meshconfig, service-resolver, ingress-gateway, service-intentions, api-gateway, bound-api-gateway, tcp-route, http-route, file-system-certificate, inline-certificate, service-defaults, sameness-group, jwt-provider, exported-services). The zero value Kind_KindUnset also falls through to the panic. In a mixed-version cluster this is typically version skew: a newer agent/client sends a kind this binary has never heard of, and the internal RPC path has no graceful fallback for unknown kinds.","triggerScenarios":"An older server converts a ConfigEntry sent by a newer agent whose proto enum contains a new kind; constructing a proto ConfigEntry without setting Kind (zero value Kind_KindUnset) and passing it to the converter; during development, adding a Kind enum value but forgetting the corresponding case in ConfigEntryToStructs.","commonSituations":"Rolling upgrades of Consul where agents and servers temporarily run different versions; test fixtures with hand-built proto entries missing the Kind field; contributors adding a new config entry kind who updated the enum but not this converter.","solutions":["Upgrade the older binaries so server and agent share the same set of kinds","If developing a new kind, add the case to ConfigEntryToStructs (and the mirror case to ConfigEntryFromStructs)","Validate Kind against the known set and return an error at the RPC/decode boundary before converting","When building proto entries in code or fixtures, always set Kind alongside Entry"],"exampleFix":"// before\nout := pbconfigentry.ConfigEntryToStructs(entry) // entry.Kind from a newer agent, or unset\n\n// after\nif !knownConfigEntryKind(entry.GetKind()) {\n\treturn nil, fmt.Errorf(\"unsupported config entry kind: %s\", entry.GetKind())\n}\nout := pbconfigentry.ConfigEntryToStructs(entry)","handlingStrategy":"validation","validationCode":"var knownConfigEntryKinds = map[pbconfigentry.Kind]bool{\n\tpbconfigentry.Kind_KindMeshConfig: true, pbconfigentry.Kind_KindServiceResolver: true,\n\tpbconfigentry.Kind_KindIngressGateway: true, pbconfigentry.Kind_KindServiceIntentions: true,\n\tpbconfigentry.Kind_KindAPIGateway: true, pbconfigentry.Kind_KindBoundAPIGateway: true,\n\tpbconfigentry.Kind_KindTCPRoute: true, pbconfigentry.Kind_KindHTTPRoute: true,\n\tpbconfigentry.Kind_KindFileSystemCertificate: true, pbconfigentry.Kind_KindInlineCertificate: true,\n\tpbconfigentry.Kind_KindServiceDefaults: true, pbconfigentry.Kind_KindSamenessGroup: true,\n\tpbconfigentry.Kind_KindJWTProvider: true, pbconfigentry.Kind_KindExportedServices: true,\n}\n\nif !knownConfigEntryKinds[entry.GetKind()] {\n\treturn nil, fmt.Errorf(\"unsupported config entry kind: %s\", entry.GetKind())\n}\nout := pbconfigentry.ConfigEntryToStructs(entry)","typeGuard":null,"tryCatchPattern":"// at an RPC handler boundary, translate the panic into an error response\ndefer func() {\n\tif r := recover(); r != nil {\n\t\terr = fmt.Errorf(\"cannot convert config entry (version skew?): %v\", r)\n\t}\n}()","preventionTips":["Reject unknown kinds at the RPC/decode boundary instead of letting the converter see them","Always set Kind when building proto ConfigEntry values; zero-value Kind is not valid","When adding a Kind enum value, update both converters and the kind-validation set in the same commit","Avoid mixed agent/server versions during rolling upgrades once new kinds exist"],"tags":["go","consul","panic","protobuf","config-entry","version-skew","serialization"],"backgroundTag":null,"analyzedSha":"2397ff0d763d34f2fe37fe59fde6a7f7fc430a3e","analyzedAt":"2026-08-15T19:19:47.700Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}