thanos-io/thanos · error
no group
Error message
no group
What it means
rulesServer.Send returns 'no group' when a RulesResponse contains neither a Warning nor a Group. The rules proxy contract requires every streamed response to carry one of the two, so an empty response indicates a protocol violation by the sender.
Solutions
- Fix the sender to always populate either Group or Warning on every response
- Check for version mismatch between the ruler sending responses and the rules proxy
- Add a test asserting every streamed RulesResponse is non-empty
Defensive patterns
Strategy: validation
Validate before calling
if res.GetWarning() == "" && res.GetGroup() == nil {
return errors.New("response must carry a group or a warning")
} Type guard
func isUsableRulesResponse(res *rulespb.RulesResponse) bool {
return res.GetWarning() != "" || res.GetGroup() != nil
} Try / catch
if err != nil && strings.Contains(err.Error(), "no group") {
log.Printf("sender emitted an empty RulesResponse: %v", err)
} Prevention
- Ensure every response written to the stream populates Group or Warning
- Pin compatible versions of ruler and rules proxy
- Add contract tests that stream at least one non-empty response
When it happens
Trigger: An upstream/component streams a rulespb.RulesResponse with both Warning and Group unset — typically a misconfigured or buggy ruler, or a hand-constructed response in tests/custom code.
Common situations: Custom ruler implementations that emit empty responses; version skew where an old sender omits fields; faulty middleware mutating the response in transit.
Related errors
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/74806b5bbbdd8a97.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/rules/rules.go:285
// This field just exist to pseudo-implement the unused methods of the interface.
rulespb.Rules_RulesServer
ctx context.Context
warnings annotations.Annotations
groups []*rulespb.RuleGroup
mu sync.Mutex
}
func (srv *rulesServer) Send(res *rulespb.RulesResponse) error {
if res.GetWarning() != "" {
srv.mu.Lock()
defer srv.mu.Unlock()
srv.warnings.Add(errors.New(res.GetWarning()))
return nil
}
if res.GetGroup() == nil {
return errors.New("no group")
}
srv.mu.Lock()
defer srv.mu.Unlock()
srv.groups = append(srv.groups, res.GetGroup())
return nil
}
func (srv *rulesServer) Context() context.Context {
return srv.ctx
}
View on GitHub (pinned to 35b8b99117)