{"record":{"id":"73cbe17a18ae5e83","repo":"jhy/jsoup","slug":"range-positions-must-be-non-negative","errorCode":null,"errorMessage":"Range positions must be non-negative","messagePattern":"Range positions must be non-negative","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/jsoup/nodes/Range.java","lineNumber":42,"sourceCode":"    private final int startPos;\n    private final int endPos;\n\n    /**\n     Creates the untracked source range sentinel.\n     */\n    private Range() {\n        lineMap = UnsetLineMap;\n        startPos = -1;\n        endPos = -1;\n    }\n\n    /**\n     Creates a new Range from source offsets.\n     */\n    private Range(LineMap lineMap, int startPos, int endPos) {\n        this.lineMap = lineMap;\n        if (startPos < 0 || endPos < 0)\n            throw new IllegalArgumentException(\"Range positions must be non-negative\");\n        this.startPos = startPos;\n        this.endPos = endPos;\n    }\n\n    /**\n     Get the start position of this range, with 1-based line and column coordinates.\n     * @return the start position.\n     */\n    public Position start() {\n        return startPos == -1 ? UntrackedPos : position(startPos);\n    }\n\n    /**\n     Get the starting source offset of this range.\n     @return the 0-based start source offset.\n     @since 1.17.1\n     */\n    public int startPos() {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/jhy/jsoup/blob/9851ac5d9c576c6888910b5a51a2362bbc978959/src/main/java/org/jsoup/nodes/Range.java#L24-L60","documentation":"Range represents a source-position span for a node when tracking is enabled. Its private constructor requires both start and end character offsets to be >= 0; negative offsets indicate corrupt position data, so jsoup throws IllegalArgumentException('Range positions must be non-negative').","triggerScenarios":"Constructing a Range (directly or via node source position setters / Line-Number tracking) with a negative startPos or endPos, usually from a miscomputed offset in custom parsing or node-movement code.","commonSituations":"Custom DocumentType/Node position bookkeeping that subtracts offsets incorrectly, integration code copying ranges between documents, or off-by-one/negative results from upstream offset math.","solutions":["Clamp or validate offsets before creating the range: ensure startPos and endPos are >= 0 and startPos <= endPos","Check the offset calculation producing negative values (e.g. subtracting a larger prefix length)","Enable jsoup's position tracking with Parser.setTrackPosition(true) and rely on jsoup's own offsets rather than hand-built ranges","Catch IllegalArgumentException around range construction when offsets come from untrusted computation"],"exampleFix":"// before\nRange r = new Range(lineMap, computedStart, computedEnd); // computedStart = -3\n// after\nif (computedStart < 0) computedStart = 0;\nRange r = new Range(lineMap, Math.max(0, computedStart), Math.max(0, computedEnd));","handlingStrategy":"validation","validationCode":"boolean validRange(int start, int end) { return start >= 0 && end >= 0 && start <= end; }","typeGuard":null,"tryCatchPattern":"try { Range r = makeRange(startPos, endPos); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"non-negative\")) { /* fix offsets or skip */ } throw e; }","preventionTips":["Clamp computed offsets with Math.max(0, x)","Verify offset arithmetic for sign errors","Rely on jsoup's trackPosition output instead of manual offsets","Unit-test position bookkeeping with edge cases (start of file, empty nodes)"],"tags":["range","source-position","validation"],"backgroundTag":"argument-out-of-range","analyzedSha":"9851ac5d9c576c6888910b5a51a2362bbc978959","analyzedAt":"2026-09-08T15:22:04.931Z","contentChangedAt":"2026-09-08T15:22:04.931Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}