jhy/jsoup · error · IllegalArgumentException

Source ranges must come from the same parse

Error message

Source ranges must come from the same parse

What it means

Internal consistency guard in Range.useLineMap: the parser attached ranges whose position data derives from more than one Document/parse. jsoup tracks character positions per parse via a single line map; once the first range's line map is retained, any subsequent range created from a different parse cannot be translated to line/column correctly. This fires when ranges from separately parsed documents (or manually fabricated Position/range data not tied to the current parse) are mixed on the same node or attribute set.

Solutions

  1. Ensure all nodes and attribute ranges come from a single Document.parse call; do not merge nodes across separately parsed documents before reading source ranges.
  2. If combining content from multiple parses, read each document's ranges before merging, or re-parse the combined input so all ranges share one line map.
  3. Avoid constructing Range/Position values manually unless they originate from the same parser instance's line map.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at src/main/java/org/jsoup/nodes/Range.java:437 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08). Data as JSON: /api/errors/32af2c35711cf4b0. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/org/jsoup/nodes/Range.java:437

                throw new IllegalArgumentException("Attribute range positions must be non-negative");
            useLineMap(lineMap);
            int nameIndex = attrNameStartIndex(index);
            int valueIndex = attrValueStartIndex(index);
            ensureAttributeCapacity(valueIndex + 2);
            attrRanges[nameIndex] = nameStart;
            attrRanges[nameIndex + 1] = nameEnd;
            attrRanges[valueIndex] = valueStart;
            attrRanges[valueIndex + 1] = valueEnd;
        }

        /**
         Retains the first line map and rejects mixed-source ranges.
         */
        private void useLineMap(LineMap lineMap) {
            if (this.lineMap == UnsetLineMap) {
                this.lineMap = lineMap;
            } else if (this.lineMap != lineMap) {
                throw new IllegalArgumentException("Source ranges must come from the same parse");
            }
        }

        /**
         Removes an attribute slot and shifts following source ranges.
         */
        void removeAttributeRange(int index) {
            if (index < 0) return;
            int[] ranges = attrRanges;
            int removeIndex = attrNameStartIndex(index);
            if (removeIndex >= ranges.length) return;

            int nextIndex = removeIndex + AttrRangeWidth;
            int shifted = ranges.length - nextIndex;
            if (shifted > 0)
                System.arraycopy(ranges, nextIndex, ranges, removeIndex, shifted);
            Arrays.fill(ranges, ranges.length - AttrRangeWidth, ranges.length, -1);
        }

View on GitHub (pinned to 9851ac5d9c)