[bug-hunter] BeatReceiver.Start panics on Run error when beater lacks OTEL wrapper
- Dominant language
- Go
- Stars
- 12.7k
- Forks
- 5k
- Avg merge
- 2d 15m
- Merged PRs (30d)
- 385
Description
## Impact
A receiver startup failure can crash with a panic instead of returning an error. This turns a recoverable startup error into process termination for beat receivers that do not implement `cfgfile.WithOtelFactoryWrapper`.
## Reproduction Steps
1. Create this new test file at `x-pack/libbeat/cmd/instance/repro_receiver_panic_test.go`:
```go
package instance
import (
"errors"
"testing"
"github.com/elastic/beats/v7/libbeat/beat"
beatinstance "github.com/elastic/beats/v7/libbeat/cmd/instance"
"github.com/elastic/beats/v7/libbeat/management"
"github.com/elastic/elastic-agent-libs/config"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/component/componentstatus"
)
type failingBeater struct{}
func (f *failingBeater) Run(*beat.Beat) error { return errors.New("boom") }
func (f *failingBeater) Stop() {}
type emptyHost struct{}
func (h *emptyHost) GetExtensions() map[component.ID]component.Component { return nil }
func (h *emptyHost) Report(*componentstatus.Event) {}
// Expected behavior: Start should return an error when Run fails.
// Actual behavior: Start panics because groupReporter is nil.
func TestReproStartShouldReturnErrorInsteadOfPanic(t *testing.T) {
disableMetricsLog, err := config.NewConfigFrom(map[string]interface{}{"enabled": false})
if err != nil {
t.Fatalf("config setup failed: %v", err)
}
b := &beatinstance.Beat{}
b.Manager = &management.FallbackManager{}
b.Config.MetricLogging = disableMetricsLog
br := &BeatReceiver{beat: b, beater: &failingBeater{}}
if err := br.Start(&emptyHost{}); err == nil {
t.Fatal("expected run error, got nil")
}
}
```
2. Run:
```bash
go test ./x-pack/libbeat/cmd/instance -run TestReproStartShouldReturnErrorInsteadOfPanic -count=1
```
## Expected vs Actual
**Expected:** `BeatReceiver.Start` returns `beat receiver run error: boom` (or equivalent wrapped error).
**Actual:** panic due to nil dereference:
```text
--- FAIL: TestReproStartShouldReturnErrorInsteadOfPanic (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked]
...
github.com/elastic/beats/v7/x-pack/libbeat/cmd/instance.(*BeatReceiver).Start(...)
/home/runner/work/beats/beats/x-pack/libbeat/cmd/instance/receiver.go:187 +0x5ef
...
FAIL github.com/elastic/beats/v7/x-pack/libbeat/cmd/instance 0.022s
```
## Failing Test
The full failing test is included in Reproduction Step 1.
## Evidence
- `x-pack/libbeat/cmd/instance/receiver.go:119-122` initializes `groupReporter` only if `br.beater` implements `cfgfile.WithOtelFactoryWrapper`.
- `x-pack/libbeat/cmd/instance/receiver.go:185-188` unconditionally calls `groupReporter.UpdateStatus(...)` on run error.
- `git blame` shows line 187 (`groupReporter.UpdateStatus`) was added in commit `2ac081b1086`, making the nil call reachable when `Run` returns an error and wrapper is not implemented.
---
[What is this?](https://ela.st/github-ai-tools) | [From workflow: Bug Hunter](https://github.com/elastic/beats/actions/runs/23191576728)
Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.
> - [x] expires on Mar 24, 2026, 12:12 PM UTC
Contributor guide
Research direction
Start with x-pack/libbeat/cmd/instance/receiver.go, especially the groupReporter initialization at lines 119-122 and the run-error path at lines 185-188. Add the regression test from the issue as x-pack/libbeat/cmd/instance/repro_receiver_panic_test.go, then run go test ./x-pack/libbeat/cmd/instance -run TestReproStartShouldReturnErrorInsteadOfPanic -count=1. Done means Start returns the run error instead of panicking when the beater lacks the OTEL wrapper.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100