greenrobot/greenDAO · error · DaoException

Expected unique result, but count was

Error message

Expected unique result, but count was ${cursor.getCount()}

What it means

greenDAO's loadUnique(Cursor) expects a query that returns at most one row. If the cursor is positioned on the first row but isLast() is false, more than one row matched, so it throws DaoException. This protects API contracts where the caller assumes a unique result.

Solutions

  1. Make the query condition actually unique (query by primary key or a column with a UNIQUE index).
  2. Use query().list()/listIterator() instead of loadUnique if multiple rows are legitimate.
  3. Deduplicate existing data with DELETE/UPDATE so at most one row matches the condition.
  4. Add a UNIQUE constraint to the column so duplicates cannot be inserted (with schema migration).

Example fix

// before
User u = userDao.queryBuilder().where(UserDao.Properties.Email.eq(email)).unique();
// after (column not guaranteed unique — handle list)
List<User> users = userDao.queryBuilder().where(UserDao.Properties.Email.eq(email)).list();
User u = users.isEmpty() ? null : users.get(0);
Defensive patterns

Strategy: validation

Validate before calling

long count = userDao.queryBuilder().where(Properties.Email.eq(email)).buildCount().count();
if (count > 1) throw new IllegalStateException("Duplicate rows for email: " + email);

Try / catch

try {
    User u = userDao.queryBuilder().where(Properties.Email.eq(email)).unique();
    // use u (may be null)
} catch (DaoException e) {
    // multiple rows matched: fall back to list query and pick/dedupe
    List<User> all = userDao.queryBuilder().where(Properties.Email.eq(email)).list();
}

Prevention

When it happens

Trigger: Calling loadUnique()/loadUniqueAndCloseCursor() on a query whose WHERE clause matches more than one row — typically a query on a non-unique column or a queryBuilder whereUnique/where condition that isn't actually unique.

Common situations: Querying by a column assumed unique but not declared UNIQUE in the schema; duplicated rows after a schema migration or manual inserts; using loadUnique where load() by primary key or a list query was intended.

Related errors


AI-assisted analysis of greenrobot/greenDAO@0bbb338e17 (2026-09-08). Data as JSON: /api/errors/b54527c247fdf47c. Report an issue: GitHub.

Appendix: source

Thrown at DaoCore/src/main/java/org/greenrobot/greendao/AbstractDao.java:170

        String[] idArray = new String[]{Long.toString(rowId)};
        Cursor cursor = db.rawQuery(statements.getSelectByRowId(), idArray);
        return loadUniqueAndCloseCursor(cursor);
    }

    protected T loadUniqueAndCloseCursor(Cursor cursor) {
        try {
            return loadUnique(cursor);
        } finally {
            cursor.close();
        }
    }

    protected T loadUnique(Cursor cursor) {
        boolean available = cursor.moveToFirst();
        if (!available) {
            return null;
        } else if (!cursor.isLast()) {
            throw new DaoException("Expected unique result, but count was " + cursor.getCount());
        }
        return loadCurrent(cursor, 0, true);
    }

    /** Loads all available entities from the database. */
    public List<T> loadAll() {
        Cursor cursor = db.rawQuery(statements.getSelectAll(), null);
        return loadAllAndCloseCursor(cursor);
    }

    /** Detaches an entity from the identity scope (session). Subsequent query results won't return this object. */
    public boolean detach(T entity) {
        if (identityScope != null) {
            K key = getKeyVerified(entity);
            return identityScope.detach(key, entity);
        } else {
            return false;
        }

View on GitHub (pinned to 0bbb338e17)