aeron-io/aeron · error · IllegalArgumentException

EINVAL

EINVAL

Error message

Unknown correlation id

What it means

Thrown by Publication::findDestinationResponse(correlationId) when the correlation id is not found among the publication's pending destinations (m_pendingDestinations), while holding the admin lock. Each async addDestination/removeDestination registers an id that is consumed exactly once; querying an unregistered or already-resolved id throws. The library signals this as EINVAL rather than a not-found return value.

Solutions

  1. Keep a one-to-one record of ids returned by addDestination/removeDestination and discard each id after its first successful findDestinationResponse.
  2. Confirm the id belongs to this exact Publication instance (not a Subscription or ExclusivePublication).
  3. After a restart, re-issue add/removeDestination rather than reusing persisted correlation ids.
  4. Guard with try-catch on IllegalArgumentException and fall back to re-registering the destination.

Example fix

// before
publication.findDestinationResponse(id);
// ... later
publication.findDestinationResponse(id); // second poll of same id throws
// after
if (publication.findDestinationResponse(id)) { id = -1; /* mark consumed */ }
Defensive patterns

Strategy: validation

Validate before calling

std::unordered_map<long, std::string> pending;
long id = publication.addDestination(uri);
pending[id] = uri;
// before querying: if (!pending.count(id)) { /* re-add instead */ }

Try / catch

try {
  if (publication.findDestinationResponse(id)) { pending.erase(id); }
} catch (const aeron::util::IllegalArgumentException&) {
  log("unknown correlation id; re-registering destination");
}

Prevention

When it happens

Trigger: Calling findDestinationResponse with an id not obtained from this Publication's addDestination/removeDestination, polling twice with the same id, or passing a Subscription or ExclusivePublication id to a different object.

Common situations: Correlation-id bookkeeping bugs in application code, duplicate polling loops that never clear consumed ids, or IDs persisted and replayed after a restart where pending state is gone.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12). Data as JSON: /api/errors/0c7de099cf5da29f. Report an issue: GitHub.

Appendix: source

Thrown at aeron-client/src/main/cpp_wrapper/Publication.h:755

     * - If the media driver has not answered the add/remove command, then a false is returned.
     * - If the media driver has successfully added or removed the destination then true is returned.
     * - If the media driver has returned an error, this method will throw the error returned.
     *
     * @see Publication::addDestination
     * @see Publication::removeDestination
     *
     * @param correlationId of the add/remove command returned by Publication::addDestination
     * or Publication::removeDestination
     * @return true for added or false if not.
     */
    bool findDestinationResponse(std::int64_t correlationId)
    {
        std::lock_guard<std::recursive_mutex> lock(m_adminLock);

        auto search = m_pendingDestinations.find(correlationId);
        if (search == m_pendingDestinations.end())
        {
            throw IllegalArgumentException("Unknown correlation id", SOURCEINFO, EINVAL);
        }

        auto async = search->second;
        try
        {
            bool result = findDestinationResponse(async);
            if (result)
            {
                m_pendingDestinations.erase(correlationId);
            }
            return result;
        }
        catch (...)
        {
            m_pendingDestinations.erase(correlationId);
            throw;
        }
    }

View on GitHub (pinned to 6d60124e15)