quarkusio/quarkus · error · PanacheQueryException

There should be no more than one result

Error message

There should be no more than one result

What it means

singleResultOptional() fetches up to 2 rows and throws PanacheQueryException when the query matches more than one document (list.size() == 2). Unlike singleResult(), an empty result is acceptable (returns Optional.empty()), but duplicate matches still violate the 'at most one' contract.

Source

Thrown at extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/common/reactive/runtime/CommonReactivePanacheQueryImpl.java:230

                ? collection.find(Panache.getCurrentSession(), getQuery(), options)
                : collection.find(getQuery(), options);
        return results.collect().asList().map(list -> {
            if (list.size() != 1) {
                throw new PanacheQueryException("There should be only one result");
            } else {
                return list.get(0);
            }
        });
    }

    public <T extends Entity> Uni<Optional<T>> singleResultOptional() {
        FindOptions options = buildOptions(2);
        Multi<T> results = Panache.getCurrentSession() != null
                ? collection.find(Panache.getCurrentSession(), getQuery(), options)
                : collection.find(getQuery(), options);
        return results.collect().asList().map(list -> {
            if (list.size() == 2) {
                throw new PanacheQueryException("There should be no more than one result");
            }
            return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0));
        });
    }

    private FindOptions buildOptions() {
        FindOptions options = new FindOptions();
        options.sort(sort);
        if (range != null) {
            // range is 0 based, so we add 1 to the limit
            options.skip(range.getStartIndex()).limit(range.getLastIndex() - range.getStartIndex() + 1);
        } else if (page != null) {
            options.skip(page.index * page.size).limit(page.size);
        }
        if (projections != null) {
            options.projection(this.projections);
        }
        if (this.collation != null) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Create a unique index on the lookup field and clean up existing duplicates
  2. Narrow the query filter (e.g. add tenant or status = ACTIVE) so only one document matches
  3. Deduplicate data before deploying the field as a logical unique key

Example fix

// before
Optional<User> u = reactiveFind("email", email).singleResultOptional(); // throws on dupes

// after
db.users.createIndex({ email: 1 }, { unique: true, collation: { locale: 'en', strength: 2 } });
Optional<User> u = reactiveFind("email", email).singleResultOptional();
Defensive patterns

Strategy: try-catch

Try / catch

try {
    return query.singleResultOptional().await().indefinitely();
} catch (PanacheQueryException e) {
    // duplicates found: log entity/field and either deduplicate or use firstResult()
    return Optional.empty();
}

Prevention

When it happens

Trigger: Calling singleResultOptional() on a query whose filter matches 2+ documents; this happens when the 'unique' field has duplicate values because MongoDB enforces no uniqueness unless an index is created.

Common situations: Duplicate records inserted before a unique index was added; case-sensitive duplicates ('Foo@x.com' vs 'foo@x.com'); soft-deleted copies of the same logical entity still present in the collection.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/488785466d182969. Report an issue: GitHub.