hashicorp/nomad · warning
ErrAllocBroadcasterClosed
ErrAllocBroadcasterClosed
Error message
alloc broadcaster closed
What it means
ErrAllocBroadcasterClosed is returned by AllocBroadcaster.Send when the broadcaster has already been closed. The broadcaster distributes the latest alloc update to listeners (capacity-1 channels) without blocking; sending after Close is a programming/lifecycle error. Errors.Is/errors.As matching works via the sentinel value.
Source
Thrown at client/structs/broadcaster.go:21
package structs
import (
"errors"
"sync"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/nomad/structs"
)
const (
// listenerCap is the capacity of the listener chans. Must be exactly 1
// to prevent Sends from blocking and allows them to pop old pending
// updates from the chan before enqueueing the latest update.
listenerCap = 1
)
var ErrAllocBroadcasterClosed = errors.New("alloc broadcaster closed")
// AllocBroadcaster implements an allocation broadcast channel where each
// listener receives allocation updates. Pending updates are dropped and
// replaced by newer allocation updates, so listeners may not receive every
// allocation update. However this ensures Sends never block and listeners only
// receive the latest allocation update -- never a stale version.
type AllocBroadcaster struct {
mu sync.Mutex
// listeners is a map of unique ids to listener chans. lazily
// initialized on first listen
listeners map[int]chan *structs.Allocation
// nextId is the next id to assign in listener map
nextId int
// closed is true if broadcaster is closed.
closed boolView on GitHub (pinned to 482b49bf1a)
Solutions
- Guard Send with the broadcaster's closed state or check ErrAllocBroadcasterClosed and treat it as benign shutdown noise
- Ensure no goroutines retain the broadcaster past its Close
- Use errors.Is(err, ErrAllocBroadcasterClosed) to filter it from real failures
- Restructure lifecycle so Senders are stopped before Close
Example fix
// before
b.Send(alloc) // panics-free but errors after Close
// after
if err := b.Send(alloc); err != nil && !errors.Is(err, structs.ErrAllocBroadcasterClosed) {
return err
} Defensive patterns
Strategy: try-catch
Try / catch
if err := broadcaster.Send(alloc); err != nil {
if errors.Is(err, structs.ErrAllocBroadcasterClosed) {
return nil // benign: broadcaster shut down, drop the update
}
return err
} Prevention
- Stop all senders before calling Close on the broadcaster
- Never retain the broadcaster beyond its owning allocation's lifecycle
- Use errors.Is against ErrAllocBroadcasterClosed to filter shutdown noise
- Centralize broadcaster lifecycle management in one owner
When it happens
Trigger: Calling b.Send(alloc) after b.Close() has been called — e.g. a late update arriving after the client shut down the allocation's broadcaster, as tested in broadcaster_test.go.
Common situations: Shutdown races where a hook or watcher still holds a reference and pushes an alloc update after Close; duplicate shutdown paths calling Close then Send.
Related errors
- ErrVolumeNameExists
- Evaluation ID not found
- Token does not match for Evaluation ID
- Evaluation ID Ack'd after Nack timer expiration
- invalid digest format
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/57aebef9d3e69919.
Report an issue: GitHub.