headroomlabs-ai/headroom · error · ValueError
A tracker with key '{tracker.key}' is already registered. Ea
Error message
A tracker with key '{tracker.key}' is already registered. Each tracker must have a unique key. What it means
ValueError raised by TrackerRegistry.register in headroom/subscription/base.py when a QuotaTracker is registered whose key already exists in the registry. Keys must be unique because the registry's get(key) lookup (linear scan over self._trackers) assumes at most one tracker per key; duplicate keys would make quota accounting ambiguous. Registration is lock-protected, so this is a race-safe check.
Source
Thrown at headroom/subscription/base.py:140
# server shutdown
await registry.stop_all()
"""
def __init__(self) -> None:
self._trackers: list[QuotaTracker] = []
self._lock = Lock()
# ------------------------------------------------------------------ #
# Registration
# ------------------------------------------------------------------ #
def register(self, tracker: QuotaTracker) -> None:
"""Register a tracker. Duplicate keys are rejected."""
with self._lock:
existing_keys = {t.key for t in self._trackers}
if tracker.key in existing_keys:
raise ValueError(
f"A tracker with key '{tracker.key}' is already registered. "
"Each tracker must have a unique key."
)
self._trackers.append(tracker)
def get(self, key: str) -> QuotaTracker | None:
"""Return the registered tracker for *key*, or ``None``."""
with self._lock:
for t in self._trackers:
if t.key == key:
return t
return None
@property
def trackers(self) -> list[QuotaTracker]:
"""Read-only snapshot of the registered tracker list."""
with self._lock:
return list(self._trackers)View on GitHub (pinned to 322425c43b)
Solutions
- Before registering, call registry.get(key) and reuse or deregister the existing tracker instead of registering again.
- Derive keys from immutable identity (provider + tenant + plan) so two trackers never share a key.
- Make registration idempotent in your wrapper: register only if get(key) is None.
Example fix
# before
registry.register(QuotaTracker(key='anthropic')) # second call -> ValueError
# after
if registry.get('anthropic') is None:
registry.register(QuotaTracker(key='anthropic')) Defensive patterns
Strategy: validation
Validate before calling
if registry.get(tracker.key) is not None:
raise RuntimeError(f'tracker {tracker.key!r} already present; reusing it')
registry.register(tracker) Try / catch
try:
registry.register(tracker)
except ValueError as e:
if 'already registered' not in str(e):
raise
existing = registry.get(tracker.key)
# reconcile state onto `existing` or drop `tracker` deliberately Prevention
- Guard registration with a get() check at every call site.
- Register trackers exactly once at process startup, not in reconnect paths.
When it happens
Trigger: Calling registry.register(tracker) twice with the same tracker.key (e.g. re-running setup without teardown); registering two different trackers constructed with the same key string, like two MonthlyQuotaTrackers for the same provider key.
Common situations: Plugins or integrations that each register their own tracker against a shared registry; retry/reconnect logic that re-runs registration on failure; multi-tenant code where the key derivation collides across tenants.
Related errors
- unknown tool {tool!r}; known: {sorted(tools)}
- archive did not contain expected member {member!r}
- unknown tool {tool!r}
- No optimizer registered for '{key}'. Available: {available}
- Copilot token exchange returned an empty token.
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/a82d9e9c9ef881f1.
Report an issue: GitHub.