{"record":{"id":"4b1651565782df33","repo":"aeron-io/aeron","slug":"counter-id-counterid-is-negative","errorCode":null,"errorMessage":"counter id <counterId> is negative","messagePattern":"counter id <counterId> is negative","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"aeron-client/src/main/java/io/aeron/AeronCounters.java","lineNumber":1662,"sourceCode":"     * @param referenceId    to set for the counter.\n     * @see CountersReader#getCounterReferenceId(int)\n     * @since 1.49.0\n     */\n    public static void setReferenceId(\n        final AtomicBuffer metaDataBuffer, final AtomicBuffer valuesBuffer, final int counterId, final long referenceId)\n    {\n        Objects.requireNonNull(metaDataBuffer);\n        Objects.requireNonNull(valuesBuffer);\n        validateCounterId(metaDataBuffer, counterId);\n\n        valuesBuffer.putLongRelease(counterOffset(counterId) + REFERENCE_ID_OFFSET, referenceId);\n    }\n\n    private static void validateCounterId(final AtomicBuffer metaDataBuffer, final int counterId)\n    {\n        if (counterId < 0)\n        {\n            throw new IllegalArgumentException(\"counter id \" + counterId + \" is negative\");\n        }\n\n        final int maxCounterId = (metaDataBuffer.capacity() / METADATA_LENGTH) - 1;\n        if (counterId > maxCounterId)\n        {\n            throw new IllegalArgumentException(\n                \"counter id \" + counterId + \" out of range: 0 - maxCounterId=\" + maxCounterId);\n        }\n    }\n}\n","sourceCodeStart":1644,"sourceCodeEnd":1673,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-client/src/main/java/io/aeron/AeronCounters.java#L1644-L1673","documentation":"validateCounterId in AeronCounters rejects any negative counterId before touching the metadata buffer. Counter ids index into the counters metadata file, so a negative id is structurally invalid. This guards the metadata-offset arithmetic (counterId * METADATA_LENGTH) from producing unsafe offsets.","triggerScenarios":"Passing a negative int as counterId to any AeronCounters public API that validates the id (e.g. label updates, metadata access). Typically from an uninitialized field, a sentinel like -1, or integer underflow when computing the id.","commonSituations":"Using -1 as a 'no counter yet' sentinel and forgetting to check it; reading counterId from a C/other-language client that used an unsigned/invalid value; arithmetic overflow producing a negative id.","solutions":["Check counterId >= 0 before calling the API, or initialize the id to a valid allocated value.","Replace -1 sentinels with Optional<Integer> or an explicit 'unallocated' boolean.","Log and inspect where the negative id originates — likely an uninitialized field or bad decode from a wire/IPC message."],"exampleFix":"// before\nint counterId = -1;\naeron.counters().updateLabel(counterId, \"x\");\n// after\nif (counterId >= 0) {\n    aeron.counters().updateLabel(counterId, \"x\");\n}","handlingStrategy":"validation","validationCode":"if (counterId < 0) throw new IllegalArgumentException(\"counterId must be >= 0: \" + counterId);","typeGuard":null,"tryCatchPattern":"try {\n    aeronCounters.updateLabel(counterId, label);\n} catch (IllegalArgumentException e) {\n    log.warn(\"invalid counter id\", e);\n}","preventionTips":["Initialize counterId fields to 0 or use Optional instead of -1 sentinels","Validate ids decoded from IPC/network messages before use","Guard against integer overflow when computing ids"],"tags":["aeron","counters","illegal-argument","bounds"],"backgroundTag":"value-out-of-range","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}