{"record":{"id":"e6a905dafe2a8f4f","repo":"pinpoint-apm/pinpoint","slug":"negative-tieoffset-tieoffset","errorCode":null,"errorMessage":"negative tieOffset:<tieOffset>","messagePattern":"negative tieOffset:<tieOffset>","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"commons-hbase/src/main/java/com/navercorp/pinpoint/common/hbase/LastRowWithTiesResultsExtractor.java","lineNumber":56,"sourceCode":"\n    private final int limit;\n    private final RowMapper<T> rowMapper;\n    private final int tieOffset;\n    private final int tieLength;\n\n    @Nullable\n    private final LastRowHandler<T> rowHandler;\n    private final ToIntFunction<T> resultSizeHandler;\n\n    public LastRowWithTiesResultsExtractor(RowMapper<T> rowMapper,\n                                           int limit,\n                                           int tieOffset,\n                                           int tieLength,\n                                           @Nullable LastRowHandler<T> rowHandler) {\n        this.rowMapper = Objects.requireNonNull(rowMapper, \"RowMapper\");\n        this.limit = limit;\n        if (tieOffset < 0) {\n            throw new IllegalArgumentException(\"negative tieOffset:\" + tieOffset);\n        }\n        this.tieOffset = tieOffset;\n        if (tieLength <= 0) {\n            throw new IllegalArgumentException(\"non-positive tieLength:\" + tieLength);\n        }\n        this.tieLength = tieLength;\n        this.rowHandler = rowHandler;\n        this.resultSizeHandler = resolveResultSizeHandler(rowMapper);\n    }\n\n    private ToIntFunction<T> resolveResultSizeHandler(RowMapper<T> rowMapper) {\n        if (rowMapper instanceof RowTypeHint hint) {\n            Class<?> clazz = hint.rowType();\n            return ResultSizeHandlers.getHandler(clazz);\n        }\n        return new LazyResultSizeHandler<>();\n    }\n","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/commons-hbase/src/main/java/com/navercorp/pinpoint/common/hbase/LastRowWithTiesResultsExtractor.java#L38-L74","documentation":"Constructor argument validation in LastRowWithTiesResultsExtractor: tieOffset must be >= 0 because it is a row offset into the tie group appended to the limited result. A negative value is a caller programming bug, so the constructor fails fast with IllegalArgumentException.","triggerScenarios":"Calling new LastRowWithTiesResultsExtractor<>(rowMapper, limit, tieOffset, tieLength, rowHandler) with a negative tieOffset (e.g. computing offset as (page-1)*size where page is 0 or negative).","commonSituations":"Pagination math bugs (page 0 offset subtraction), passing -1 as a sentinel 'no offset' value where 0 is required.","solutions":["Pass tieOffset >= 0; use 0 when no offset into the tie rows is desired","Fix the pagination arithmetic that produced the negative offset","Clamp the value: Math.max(0, tieOffset) at the call site if a sentinel -1 is possible"],"exampleFix":"// before\nnew LastRowWithTiesResultsExtractor<>(mapper, limit, -1, tieLength, handler);\n// after\nnew LastRowWithTiesResultsExtractor<>(mapper, limit, 0, tieLength, handler);","handlingStrategy":"validation","validationCode":"if (tieOffset < 0) throw new IllegalArgumentException(\"tieOffset must be >= 0, got \" + tieOffset);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Compute pagination offsets with Math.max(0, (page - 1) * size)","Never use negative sentinels for offsets; use 0 for 'none'","Add unit tests for page-0 and first-page pagination paths"],"tags":["hbase","argument-validation","pagination"],"backgroundTag":"invalid-argument-value","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}