{"record":{"id":"ccd5112d5dc0b734","repo":"greenrobot/greenDAO","slug":"unexpected-row-count","errorCode":null,"errorMessage":"Unexpected row count: ","messagePattern":"Unexpected row count: ","errorType":"exception","errorClass":"DaoException","httpStatus":null,"severity":"error","filePath":"DaoCore/src/main/java/org/greenrobot/greendao/query/CountQuery.java","lineNumber":63,"sourceCode":"\n    private CountQuery(QueryData<T> queryData, AbstractDao<T, ?> dao, String sql, String[] initialValues) {\n        super(dao, sql, initialValues);\n        this.queryData = queryData;\n    }\n\n    public CountQuery<T> forCurrentThread() {\n        return queryData.forCurrentThread(this);\n    }\n\n    /** Returns the count (number of results matching the query). Uses SELECT COUNT (*) sematics. */\n    public long count() {\n        checkThread();\n        Cursor cursor = dao.getDatabase().rawQuery(sql, parameters);\n        try {\n            if (!cursor.moveToNext()) {\n                throw new DaoException(\"No result for count\");\n            } else if (!cursor.isLast()) {\n                throw new DaoException(\"Unexpected row count: \" + cursor.getCount());\n            } else if (cursor.getColumnCount() != 1) {\n                throw new DaoException(\"Unexpected column count: \" + cursor.getColumnCount());\n            }\n            return cursor.getLong(0);\n        } finally {\n            cursor.close();\n        }\n    }\n\n    // copy setParameter methods to allow easy chaining\n    @Override\n    public CountQuery<T> setParameter(int index, Object parameter) {\n        return (CountQuery<T>) super.setParameter(index, parameter);\n    }\n\n    @Override\n    public CountQuery<T> setParameter(int index, Date parameter) {\n        return (CountQuery<T>) super.setParameter(index, parameter);","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/greenrobot/greenDAO/blob/0bbb338e17c4cf28aba5aae62d42f73f50186157/DaoCore/src/main/java/org/greenrobot/greendao/query/CountQuery.java#L45-L81","documentation":"A COUNT(*) aggregate must return exactly one row. count() checks cursor.isLast() after moveToNext(); if more than one row came back, the result set shape is unexpected and DaoException(\"Unexpected row count: N\") is thrown with the actual row count.","triggerScenarios":"The rawQuery returns multiple rows, e.g. the SQL was altered to a GROUP BY select or a non-aggregate query, breaking the single-row COUNT contract.","commonSituations":"Manually modified count SQL; custom SQLiteCursor implementations; mixing joins that produce grouped rows into a CountQuery.","solutions":["Ensure the count query SQL contains no GROUP BY and is a pure aggregate select","Rebuild via buildCount() rather than reusing a regular Query's SQL","Inspect the generated SQL and the cursor row count to find why multiple rows are produced"],"exampleFix":"// before\n// custom SQL with GROUP BY passed into a count cursor\n\"SELECT dept, COUNT(*) FROM USER GROUP BY dept\"\n// after\n\"SELECT COUNT(*) FROM USER\"","handlingStrategy":"try-catch","validationCode":"String sql = countSql.toLowerCase();\nif (sql.contains(\"group by\")) throw new IllegalStateException(\"count SQL must not GROUP BY\");","typeGuard":null,"tryCatchPattern":"try {\n    long n = countQuery.count();\n} catch (DaoException e) {\n    if (e.getMessage().startsWith(\"Unexpected row count\")) {\n        // inspect SQL for GROUP BY/joins producing multiple rows\n    }\n}","preventionTips":["Keep count SQL a pure aggregate without GROUP BY","Use buildCount() rather than adapting select queries","Test count queries after schema/join changes"],"tags":["greendao","orm","database","count"],"backgroundTag":"unexpected-response-shape","analyzedSha":"0bbb338e17c4cf28aba5aae62d42f73f50186157","analyzedAt":"2026-09-08T03:22:36.050Z","contentChangedAt":"2026-09-08T03:22:36.050Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}