{"record":{"id":"7a40069fc6218fa5","repo":"hibernate/hibernate-orm","slug":"first-result-cannot-be-negative","errorCode":null,"errorMessage":"First result cannot be negative","messagePattern":"First result cannot be negative","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/internal/SelectionQueryImpl.java","lineNumber":423,"sourceCode":"\t@Override\n\t@Nullable\n\tpublic Integer getFetchSize() {\n\t\treturn getQueryOptions().getFetchSize();\n\t}\n\n\t@Override\n\t@Nonnull\n\tpublic SelectionQueryImplementor<R> setFetchSize(int fetchSize) {\n\t\tqueryOptions.setFetchSize( fetchSize );\n\t\treturn this;\n\t}\n\n\t@Override\n\t@Nonnull\n\tpublic SelectionQueryImplementor<R> setFirstResult(int startPosition) {\n\t\tsession.checkOpen();\n\t\tif ( startPosition < 0 ) {\n\t\t\tthrow new IllegalArgumentException( \"First result cannot be negative\" );\n\t\t}\n\t\tqueryOptions.getLimit().setFirstRow( startPosition );\n\t\treturn this;\n\t}\n\n\t@Override\n\t@Nonnull\n\tpublic SelectionQueryImplementor<R> setMaxResults(int maxResults) {\n\t\tif ( maxResults < 0 ) {\n\t\t\tthrow new IllegalArgumentException( \"Max results cannot be negative\" );\n\t\t}\n\t\tsession.checkOpen();\n\t\tqueryOptions.getLimit().setMaxRows( maxResults );\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic SelectionQueryImplementor<R> setPage(Page page) {","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/internal/SelectionQueryImpl.java#L405-L441","documentation":"SelectionQueryImpl.setFirstResult checks the session is open and then requires startPosition >= 0, throwing IllegalArgumentException for any negative value. Negative offsets have no meaning in the generated pagination SQL (offset/limit), so they are rejected outright rather than clamped.","triggerScenarios":"Calling setFirstResult(-1) directly, or more often setFirstResult((page - 1) * size) with page=0 in a 1-based UI, or a user-supplied page parameter parsed without validation (e.g. ?page=-2).","commonSituations":"Web pagination controllers mixing 0-based and 1-based page indices; computed offsets underflowing to negative; using -1 as an 'unset' sentinel default that leaks into the call.","solutions":["Clamp the offset: setFirstResult(Math.max(0, offset)).","Validate and normalize page/size at the controller boundary (page >= 1 or >= 0 consistently, size > 0).","Do not use -1 as a sentinel for 'no offset'; use 0 or skip the call."],"exampleFix":"// before\nint page = 0; // user sent page=0 in a 1-based UI\nq.setFirstResult((page - 1) * size); // -size -> throws\n\n// after\nint page = Math.max(1, requestedPage);\nq.setFirstResult((page - 1) * size);","handlingStrategy":"validation","validationCode":"static int safeFirstResult(long page, int size) {\n    if (page < 1 || size < 1) throw new IllegalArgumentException(\"page >= 1 and size >= 1 required\");\n    return (int) Math.max(0, (page - 1) * size);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Normalize page/size once at the controller boundary.","Never use -1 as a sentinel offset; default to 0 or skip setFirstResult.","Standardize on 1-based or 0-based page indices across the whole API."],"tags":["hibernate","pagination","setfirstresult","illegal-argument","input-validation"],"backgroundTag":"invalid-pagination-offset","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}