flowable/flowable-engine · error · ActivitiException

Query return " + results.size() + " results instead of max 1

Error message

Query return " + results.size() + " results instead of max 1

What it means

AbstractNativeQuery.executeSingleResult() backs nativeQuery().singleResult(). If executeList returns more than one row it throws plain ActivitiException "Query return N results instead of max 1", because singleResult() has a stricter contract: exactly one row expected. This is a query-contract violation, not an argument error.

Solutions

  1. Tighten the native query WHERE clause so it can match at most one row (filter by primary key/unique column).
  2. Use list() / listPage(0, 1) instead of singleResult() when multiple matches are legitimate, and pick the first.
  3. Add a deterministic ORDER BY + limiting filter if the newest/oldest row is wanted (e.g. RES_ID_ = ? or ORDER BY ... then listPage(0,1)).
  4. Check data for unintended duplicates.

Example fix

// before
Task t = taskService.createNativeTaskQuery()
    .sql("SELECT * FROM ACT_RU_TASK WHERE NAME_ = #{name}")
    .parameter("name", name).singleResult(); // may match many
// after
List<Task> ts = taskService.createNativeTaskQuery()
    .sql("SELECT * FROM ACT_RU_TASK WHERE NAME_ = #{name} ORDER BY CREATE_TIME_ DESC")
    .parameter("name", name).listPage(0, 1);
Task t = ts.isEmpty() ? null : ts.get(0);
Defensive patterns

Strategy: try-catch

Validate before calling

// verify uniqueness first
Long n = nativeQuery.sql(sameSql).count(); // or run list() and check size <= 1

Try / catch

try { return query.singleResult(); } catch (ActivitiException e) { if (e.getMessage().contains("results instead of max 1")) { return query.listPage(0,1).stream().findFirst().orElse(null); } throw e; }

Prevention

When it happens

Trigger: Calling nativeQuery().singleResult() where the native SQL matches multiple rows (e.g. missing unique filter like id = ? or an unprescribed where clause).

Common situations: Native queries written against tables with several matching records (multiple historic instances, versions, or active jobs); duplicated data in custom tables; missing LIMIT because singleResult was assumed to limit the query (it does not limit semantically — it fetches all and checks).

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/6391c86088eb2646. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/AbstractNativeQuery.java:168

        }
    }

    public abstract long executeCount(CommandContext commandContext, Map<String, Object> parameterMap);

    /**
     * Executes the actual query to retrieve the list of results.
     *
     * @param maxResults
     * @param firstResult
     */
    public abstract List<U> executeList(CommandContext commandContext, Map<String, Object> parameterMap, int firstResult, int maxResults);

    public U executeSingleResult(CommandContext commandContext) {
        List<U> results = executeList(commandContext, getParameterMap(), 0, Integer.MAX_VALUE);
        if (results.size() == 1) {
            return results.get(0);
        } else if (results.size() > 1) {
            throw new ActivitiException("Query return " + results.size() + " results instead of max 1");
        }
        return null;
    }

    private Map<String, Object> getParameterMap() {
        HashMap<String, Object> parameterMap = new HashMap<>();
        parameterMap.put("sql", sqlStatement);
        parameterMap.putAll(parameters);
        return parameterMap;
    }

    public Map<String, Object> getParameters() {
        return parameters;
    }

}

View on GitHub (pinned to d6d39ce1c6)