XTLS/Xray-core · error
Channel %s already registered.
Error message
Channel %s already registered.
What it means
Returned by stats.Manager.RegisterChannel when a stats channel with the same name already exists. Channels are pub/sub buses (used e.g. for log or event streaming) keyed by name; note that if the manager is running, RegisterChannel also starts the channel, so duplicates would double-start the bus — hence the exclusive check. Idempotent alternative: GetOrRegisterChannel.
Source
Thrown at app/stats/stats.go:169
// VisitOnlineMaps calls visitor function on all managed online maps.
// The visitor runs under a read lock; it must not call RegisterOnlineMap or UnregisterOnlineMap (would deadlock).
func (m *Manager) VisitOnlineMaps(visitor func(string, stats.OnlineMap) bool) {
m.access.RLock()
defer m.access.RUnlock()
for name, om := range m.onlineMaps {
if !visitor(name, om) {
break
}
}
}
// RegisterChannel implements stats.Manager.
func (m *Manager) RegisterChannel(name string) (stats.Channel, error) {
m.access.Lock()
defer m.access.Unlock()
if _, found := m.channels[name]; found {
return nil, errors.New("Channel ", name, " already registered.")
}
errors.LogDebug(context.Background(), "create new channel ", name)
c := NewChannel(&ChannelConfig{BufferSize: 64, Blocking: false})
m.channels[name] = c
if m.running {
return c, c.Start()
}
return c, nil
}
// GetOrRegisterChannel implements stats.Manager.
func (m *Manager) GetOrRegisterChannel(name string) (stats.Channel, error) {
m.access.Lock()
defer m.access.Unlock()
if c, found := m.channels[name]; found {
return c, nil
}View on GitHub (pinned to 7d214f8b09)
Solutions
- Use GetOrRegisterChannel(name) to share the existing channel instance.
- Verify teardown: Unregister/remove the channel before re-registering after a config reload.
- Namespace channel names per component if simultaneous independent channels are intended.
Example fix
// before
ch, err := statsManager.RegisterChannel("events")
// after
ch, err := statsManager.GetOrRegisterChannel("events") Defensive patterns
Strategy: fallback
Validate before calling
if _, found := m.GetChannel(name); found { /* reuse existing channel */ } Try / catch
ch, err := m.RegisterChannel(name)
if err != nil {
if existing, ok2 := m.GetChannel(name); ok2 { ch = existing } else { return err }
} Prevention
- Use GetOrRegisterChannel for well-known shared channels
- Namespace channel names per integrating component
When it happens
Trigger: Calling RegisterChannel(name) twice — e.g. two components (an API logger and a panel plugin) both registering a channel named 'logger', or re-initializing a feature after config reload on a shared Manager.
Common situations: Integrating multiple third-party extensions that assume they own a well-known channel name; reloading configurations in-process without creating a fresh stats Manager.
Related errors
- Number of subscribers has reached limit
- Counter %s already registered.
- OnlineMap %s already registered.
- metrics must have a tag or listen address
- Cannot get depended features
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/ce9a0e7c24184f26.
Report an issue: GitHub.