{"record":{"id":"4e96fc00db530830","repo":"jhy/jsoup","slug":"attribute-range-positions-must-be-non-negative","errorCode":null,"errorMessage":"Attribute range positions must be non-negative","messagePattern":"Attribute range positions must be non-negative","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/jsoup/nodes/Range.java","lineNumber":264,"sourceCode":"\n        /**\n         Creates the untracked attribute source range sentinel.\n         */\n        private AttributeRange() {\n            lineMap         = UnsetLineMap;\n            nameStartPos    = -1;\n            nameEndPos      = -1;\n            valueStartPos   = -1;\n            valueEndPos     = -1;\n        }\n\n        /**\n         Creates a new AttributeRange from source offsets.\n         */\n        private AttributeRange(LineMap lineMap, int nameStartPos, int nameEndPos, int valueStartPos, int valueEndPos) {\n            this.lineMap = lineMap;\n            if (nameStartPos < 0 || nameEndPos < 0 || valueStartPos < 0 || valueEndPos < 0)\n                throw new IllegalArgumentException(\"Attribute range positions must be non-negative\");\n            this.nameStartPos = nameStartPos;\n            this.nameEndPos = nameEndPos;\n            this.valueStartPos = valueStartPos;\n            this.valueEndPos = valueEndPos;\n        }\n\n        /** Get the source range for the attribute's name. */\n        public Range nameRange() {\n            return isTracked() ? new Range(lineMap, nameStartPos, nameEndPos) : Range.Untracked;\n        }\n\n        /** Get the source range for the attribute's value. */\n        public Range valueRange() {\n            return isTracked() ? new Range(lineMap, valueStartPos, valueEndPos) : Range.Untracked;\n        }\n\n        /**\n         Tests if this attribute range has tracked name and value offsets.","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/jhy/jsoup/blob/9851ac5d9c576c6888910b5a51a2362bbc978959/src/main/java/org/jsoup/nodes/Range.java#L246-L282","documentation":"Range.AttributeRange records source offsets for an attribute's name and value. Its private constructor validates all four offsets; any negative value throws IllegalArgumentException('Attribute range positions must be non-negative').","triggerScenarios":"Constructing an AttributeRange with a negative nameStartPos, nameEndPos, valueStartPos, or valueEndPos — typically from custom attribute-position bookkeeping or copying positions from another document.","commonSituations":"Custom parsers or transformer code that assigns attribute ranges, offset math that yields -1 when a token wasn't found, or mixing ranges across documents parsed without position tracking.","solutions":["Validate all four offsets are >= 0 before constructing; clamp negatives to 0 or skip assigning a range","Fix the offset computation returning -1 (e.g. indexOf miss) upstream","Use ranges produced by jsoup's own position tracking (Parser.setTrackPosition(true)) instead of manual values","Wrap construction in try-catch and degrade to an unpositioned attribute on failure"],"exampleFix":"// before\nattr.attributeRange(lineMap, nameStart, nameEnd, valStart, valEnd); // nameStart = -1\n// after\nif (nameStart >= 0 && nameEnd >= 0 && valStart >= 0 && valEnd >= 0)\n    attr.attributeRange(lineMap, nameStart, nameEnd, valStart, valEnd);","handlingStrategy":"validation","validationCode":"boolean validAttrRange(int ns, int ne, int vs, int ve) { return ns >= 0 && ne >= 0 && vs >= 0 && ve >= 0; }","typeGuard":null,"tryCatchPattern":"try { attr.attributeRange(lineMap, ns, ne, vs, ve); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"Attribute range\")) { /* skip or default positions */ } throw e; }","preventionTips":["Check scanner indexOf results for -1 before use","Validate all four offsets before assignment","Use jsoup-generated ranges where possible","Guard custom TreeBuilder code with offset assertions"],"tags":["range","attributes","source-position"],"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"}