aeron-io/aeron · error · util::IllegalArgumentException
EINVAL
EINVAL
Error message
counter id {id} out of range: maxCounterId={maxCounterId} What it means
CountersReader::getCounterState() validates the counter id via the C API and throws IllegalArgumentException (EINVAL) when the id is outside the valid range (0 .. maxCounterId()-1, derived from the counter metadata buffer size). The underlying aeron_counters_reader_counter_state returns < 0 for any id that has no metadata slot.
Solutions
- Validate 0 <= id < countersReader.maxCounterId() before any counter lookup.
- Look up counters by label/typeId via forEach/findByLabel instead of persisted raw ids.
- Re-acquire counter ids from the current driver session after a restart rather than reusing cached values.
- Check for negative ids from signed arithmetic before calling the API.
Example fix
// before
auto state = countersReader.getCounterState(cachedId); // cachedId may be stale
// after
if (cachedId < 0 || cachedId >= countersReader.maxCounterId())
throw std::runtime_error("counter id out of range");
auto state = countersReader.getCounterState(cachedId); Defensive patterns
Strategy: validation
Validate before calling
bool counterIdValid(const aeron::concurrent::CountersReader& r, std::int32_t id) {
return id >= 0 && id < r.maxCounterId();
} Try / catch
try {
auto state = countersReader.getCounterState(id);
} catch (const aeron::util::IllegalArgumentException& e) {
// id stale/out of range: re-enumerate counters via for-each
} Prevention
- Validate counter ids against maxCounterId() before any lookup.
- Never persist counter ids across driver restarts; look them up by label/typeId.
- Re-acquire ids from the live CountersReader after reconnecting.
When it happens
Trigger: Calling getCounterState(), getRecordingId(), getSourceIdentity(), isActive(), or byCounterId-style iteration with id < 0 or id >= maxCounterId(); using a counter id from a different, stale Aeron client or a different media driver run.
Common situations: Persisting counter ids across driver restarts; iterating with an upper bound taken from a different CountersReader; sign errors making id negative; hard-coded counter ids that depend on allocation order.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- bindAddressAndPort value too long:
- counter id is negative
- counter id is not allocated, state
- counter id out of range: 0 - maxCounterId=
- Counter not allocated: id=
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/e86bfc38255b7463.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-client/src/main/cpp_wrapper/concurrent/CountersReader.h:191
inline std::int64_t getCounterOwnerId(std::int32_t id) const
{
validateCounterId(id);
std::int64_t ownerId;
if (aeron_counters_reader_counter_owner_id(m_countersReader, id, &ownerId) < 0)
{
AERON_MAP_ERRNO_TO_SOURCED_EXCEPTION_AND_THROW;
}
return ownerId;
}
inline std::int32_t getCounterState(std::int32_t id) const
{
std::int32_t state;
if (aeron_counters_reader_counter_state(m_countersReader, id, &state) < 0)
{
throw util::IllegalArgumentException(
"counter id " + std::to_string(id) +
" out of range: maxCounterId=" + std::to_string(maxCounterId()),
SOURCEINFO, EINVAL);
}
return state;
}
inline std::int32_t getCounterTypeId(std::int32_t id) const
{
std::int32_t typeId;
if (aeron_counters_reader_counter_type_id(m_countersReader, id, &typeId) < 0)
{
throw util::IllegalArgumentException(
"counter id " + std::to_string(id) +
" out of range: maxCounterId=" + std::to_string(maxCounterId()),
SOURCEINFO, EINVAL);
}View on GitHub (pinned to 6d60124e15)