{"record":{"id":"662f186c2012df01","repo":"apache/pulsar","slug":"invalid-range-range","errorCode":null,"errorMessage":"Invalid range ${range}","messagePattern":"Invalid range (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerImpl.java","lineNumber":4053,"sourceCode":"    }\n\n    /**\n     * Get the number of entries between a contiguous range of two positions.\n     *\n     * @param range\n     *            the position range\n     * @return the count of entries\n     */\n    public long getNumberOfEntries(Range<Position> range) {\n        Position fromPosition = range.lowerEndpoint();\n        boolean fromIncluded = range.lowerBoundType() == BoundType.CLOSED;\n        Position toPosition = range.upperEndpoint();\n        boolean toIncluded = range.upperBoundType() == BoundType.CLOSED;\n        if (comparePositions(fromPosition, toPosition) > 0) {\n            log.warn().attr(\"fromPosition\", fromPosition)\n                    .attr(\"toPosition\", toPosition)\n                    .log(\"Getting number of entries with an invalid range\");\n            throw new IllegalArgumentException(\"Invalid range \" + range);\n        }\n\n        // 1. If the \"fromPosition\" is after \"toPosition\", then there is no entry in the range.\n        // 2. If both \"formPosition\" and \"toPosition\" have negative entry id amd in the same ledger, then there is no\n        //    entry in the range.\n        if (fromPosition.getLedgerId() > toPosition.getLedgerId()\n            || (fromPosition.getLedgerId() == toPosition.getLedgerId()\n                && fromPosition.getEntryId() > toPosition.getEntryId())\n            || (fromPosition.getLedgerId() == toPosition.getLedgerId()\n                && fromPosition.getEntryId() < 0 && toPosition.getEntryId() < 0)) {\n            return 0;\n        }\n\n        // If the 2 positions are in the same ledger.\n        if (fromPosition.getLedgerId() == toPosition.getLedgerId()) {\n            LedgerInfo li = ledgers.get(toPosition.getLedgerId());\n            if (li != null) {\n                // If the 2 positions are in the same ledger","sourceCodeStart":4035,"sourceCodeEnd":4071,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerImpl.java#L4035-L4071","documentation":"ManagedLedgerImpl.getNumberOfEntries(Range<Position>) validates the range after comparing endpoints; if fromPosition is greater than toPosition, the range is invalid and IllegalArgumentException is thrown. The library requires lower-to-upper ordered, correctly bounded ranges.","triggerScenarios":"Calling getNumberOfEntries with a Range whose lower endpoint is after its upper endpoint (e.g. from > to because cursor positions were computed in reverse), or building Range directly from mark-delete/read positions without ordering them first.","commonSituations":"Stats/monitoring code computing backlog over a reversed range after cursor rewinds; comparing positions across ledgers incorrectly; using Guava Range with swapped endpoints.","solutions":["Order the endpoints before constructing the Range: use Ordering.natural() or comparePositions to pick lower/upper","Return 0 or clamp the range when from > to instead of calling the API","Verify the position source (cursor state) — a rewind can invert expected order"],"exampleFix":"// before\nRange<Position> r = Range.closed(readPos, markDeletePos); // may be reversed\n// after\nRange<Position> r = fromPos.compareTo(toPos) <= 0\n    ? Range.closed(fromPos, toPos)\n    : Range.closed(toPos, fromPos);","handlingStrategy":"validation","validationCode":"if (fromPos.compareTo(toPos) > 0) {\n    Position tmp = fromPos; fromPos = toPos; toPos = tmp; // normalize order\n}\nRange<Position> range = Range.closed(fromPos, toPos);","typeGuard":null,"tryCatchPattern":"try {\n    return ml.getNumberOfEntries(range);\n} catch (IllegalArgumentException e) {\n    log.warn(\"invalid entry range: {}\", range, e);\n    return 0L;\n}","preventionTips":["Normalize endpoint order before building the Range","Account for cursor rewinds that invert position order","Use Range.closed/closedOpen consistently","Clamp ranges to the ledger's [first, last] positions"],"tags":["illegal-argument","range","positions","validation"],"backgroundTag":"invalid-argument-range","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}