greenrobot/greenDAO · error · IllegalStateException
Offset cannot be set without limit
Error message
Offset cannot be set without limit
What it means
SQL requires that OFFSET only appears together with LIMIT. QueryBuilder.checkAddOffset() enforces this when building the SQL: if offset was set via offset(offset) but limit was never set, an IllegalStateException is thrown. The check exists because the generated SQL 'OFFSET ?' without LIMIT is invalid in SQLite.
Solutions
- Always call .limit(n) before or after .offset(n) on the QueryBuilder.
- Compute page size from your paging constant instead of leaving limit unset.
- If you truly want 'skip first N', combine offset(N).limit(aLargeNumber) since SQLite needs a LIMIT.
Example fix
// before List<User> users = userDao.queryBuilder().offset(20).list(); // IllegalStateException // after List<User> users = userDao.queryBuilder().limit(10).offset(20).list();
Defensive patterns
Strategy: validation
Validate before calling
QueryBuilder<User> qb = userDao.queryBuilder();
if (qb.build().getSql() != null && offsetSet && limitNotSet) throw new IllegalArgumentException("set limit before offset"); Try / catch
try { list = qb.list(); } catch (IllegalStateException e) { list = qb.limit(pageSize).offset(offset).list(); } Prevention
- Centralize paging in a helper that always sets limit+offset together.
- Never call offset() without a page-size constant.
- Write a unit test asserting generated SQL contains both LIMIT and OFFSET.
When it happens
Trigger: queryBuilder.offset(10).list() or build() without calling limit() first — offsetPosition() / checkAddOffset during SQL construction.
Common situations: Pagination code that sets offset but forgets the page size (limit), often after refactoring where the limit call was removed or moved behind a conditional.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- JOINs are not supported for DELETE queries
- Property '
- Table alias required
- This operation only works with cached lazy lists
- Could not move to cursor location
AI-assisted analysis of greenrobot/greenDAO@0bbb338e17 (2026-09-08).
Data as JSON: /api/errors/efde13b9805f3b08.
Report an issue: GitHub.
Appendix: source
Thrown at DaoCore/src/main/java/org/greenrobot/greendao/query/QueryBuilder.java:333
}
return builder;
}
private int checkAddLimit(StringBuilder builder) {
int limitPosition = -1;
if (limit != null) {
builder.append(" LIMIT ?");
values.add(limit);
limitPosition = values.size() - 1;
}
return limitPosition;
}
private int checkAddOffset(StringBuilder builder) {
int offsetPosition = -1;
if (offset != null) {
if (limit == null) {
throw new IllegalStateException("Offset cannot be set without limit");
}
builder.append(" OFFSET ?");
values.add(offset);
offsetPosition = values.size() - 1;
}
return offsetPosition;
}
/**
* Builds a reusable query object for deletion (Query objects can be executed more efficiently than creating a
* QueryBuilder for each execution.
*/
public DeleteQuery<T> buildDelete() {
if (!joins.isEmpty()) {
throw new DaoException("JOINs are not supported for DELETE queries");
}
String tablename = dao.getTablename();
String baseSql = SqlUtils.createSqlDelete(tablename, null);View on GitHub (pinned to 0bbb338e17)