litedb-org/LiteDB · error · ArgumentException

HAVING already defined in this query

Error message

HAVING already defined in this query

What it means

Thrown by LiteQueryable.Having(BsonExpression) when _query.Having is already non-null. Like GroupBy, Having is stored as a single optional expression, so only one HAVING clause per query is supported. Having is intended to filter aggregated groups and must follow a GroupBy.

Source

Thrown at LiteDB/Client/Database/LiteQueryable.cs:210

        /// </summary>
        public ILiteQueryable<T> GroupBy(BsonExpression keySelector)
        {
            if (_query.GroupBy != null) throw new ArgumentException("GROUP BY already defined in this query");

            _query.GroupBy = keySelector;
            return this;
        }

        #endregion

        #region Having

        /// <summary>
        /// Filter documents after group by pipe according to predicate expression (requires GroupBy and support only one Having)
        /// </summary>
        public ILiteQueryable<T> Having(BsonExpression predicate)
        {
            if (_query.Having != null) throw new ArgumentException("HAVING already defined in this query");

            _query.Having = predicate;
            return this;
        }

        #endregion

        #region Select

        /// <summary>
        /// Transform input document into a new output document. Can be used with each document, group by or all source
        /// </summary>
        public ILiteQueryable<BsonDocument> Select(BsonExpression selector)
        {
            _query.Select = selector;

            return new LiteQueryable<BsonDocument>(_engine, _mapper, _collection, _query);
        }

View on GitHub (pinned to f906a5f850)

Solutions

  1. Merge multiple predicates into one Having expression joined with AND, e.g. Having("$.sum > 10 AND $.count < 5").
  2. Build the combined predicate string once before calling Having.
  3. If predicate parts come from optional inputs, concatenate only the non-null fragments.

Example fix

// before
var q = col.Query()
    .GroupBy("$.Category")
    .Having("$.sum > 10")
    .Having("$.count < 5");

// after
var q = col.Query()
    .GroupBy("$.Category")
    .Having("$.sum > 10 AND $.count < 5");
Defensive patterns

Strategy: validation

Validate before calling

// Accumulate Having fragments and join with AND once.
var predicates = new List<string>();
if (minSum.HasValue) predicates.Add($"$.sum > {minSum}");
if (maxCount.HasValue) predicates.Add($"$.count < {maxCount}");
if (predicates.Count > 0) q.Having(string.Join(" AND ", predicates));

Prevention

When it happens

Trigger: Chaining two Having calls on the same query: col.Query().GroupBy("$.Category").Having("$.sum > 10").Having("$.count < 5").

Common situations: Adding multiple post-aggregation filters by chaining (mimicking SQL AND semantics), or appending a Having from a filter-composition helper that runs more than once.

Related errors


AI-assisted analysis of litedb-org/LiteDB@f906a5f850 (2026-08-13). Data as JSON: /api/errors/5708b04994da3ec9. Report an issue: GitHub.