{"record":{"id":"5dab94ef6c1948ea","repo":"micro/go-micro","slug":"require-at-least-one-node","errorCode":null,"errorMessage":"require at least one node","messagePattern":"require at least one node","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"registry/consul/consul.go","lineNumber":156,"sourceCode":"\n\t// set the config\n\tc.config = config\n\n\t// remove client\n\tc.client = nil\n\n\t// setup the client\n\tc.Client()\n}\n\nfunc (c *consulRegistry) Init(opts ...registry.Option) error {\n\tconfigure(c, opts...)\n\treturn nil\n}\n\nfunc (c *consulRegistry) Deregister(s *registry.Service, opts ...registry.DeregisterOption) error {\n\tif len(s.Nodes) == 0 {\n\t\treturn errors.New(\"require at least one node\")\n\t}\n\n\t// delete our hash and time check of the service\n\tc.Lock()\n\tdelete(c.register, s.Name)\n\tdelete(c.lastChecked, s.Name)\n\tc.Unlock()\n\n\tnode := s.Nodes[0]\n\treturn c.Client().Agent().ServiceDeregister(node.Id)\n}\n\nfunc (c *consulRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {\n\tif len(s.Nodes) == 0 {\n\t\treturn errors.New(\"require at least one node\")\n\t}\n\n\tvar regTCPCheck bool","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/registry/consul/consul.go#L138-L174","documentation":"consulRegistry.Deregister requires the registry.Service being deregistered to carry at least one node, because deregistration is performed per node ID against the Consul agent. A service with zero nodes has nothing identifiable to remove, so the call is rejected up front.","triggerScenarios":"Calling Deregister with a *registry.Service constructed by hand (e.g. &registry.Service{Name:\"foo\"}) with an empty Nodes slice; passing a service obtained from a source that stripped nodes.","commonSituations":"Shutdown handlers deregistering a locally built service struct; tests reusing a Service value whose Nodes were cleared; deserialization of service metadata that omitted nodes.","solutions":["Populate s.Nodes with the node(s) originally registered (including correct node.Id) before calling Deregister.","Fetch the current service via the registry (GetService) to get a Nodes-populated value, then deregister it.","Skip the deregistration call when len(s.Nodes)==0 instead of invoking it.","Ensure node.Id matches the ID used at registration, otherwise Consul deregisters nothing even with nodes set."],"exampleFix":"// before\nreg.Deregister(&registry.Service{Name: \"foo\"}) // panics into error: no nodes\n// after\nsvcs, _ := reg.GetService(\"foo\")\nfor _, s := range svcs {\n    reg.Deregister(s)\n}","handlingStrategy":"validation","validationCode":"func canDeregister(s *registry.Service) bool {\n    return s != nil && s.Name != \"\" && len(s.Nodes) > 0\n}","typeGuard":"func hasNodes(s *registry.Service) bool { return s != nil && len(s.Nodes) > 0 }","tryCatchPattern":"if err := reg.Deregister(svc); err != nil && strings.Contains(err.Error(), \"require at least one node\") {\n    log.Warn(\"skipping deregister: service has no nodes\", \"service\", svc.Name)\n    return nil\n} else if err != nil { return err }","preventionTips":["Always deregister with the full Service object from GetService, not a hand-built one","Keep node.Id from registration intact through shutdown paths","Skip deregistration when Nodes is empty and log instead","Add a unit test asserting Nodes are preserved in your shutdown flow"],"tags":["consul","registry","deregister","validation"],"backgroundTag":"missing-service-nodes","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}