Tencent/WeKnora · error

empty logical condition

Error message

empty logical condition

What it means

convertLogicalCondition builds a Milvus boolean expression from an AND/OR condition's children. It returns this error when, after iterating over the condition's children, no convertResult was produced (e.g. the children array was empty), so there is no expression string to emit.

Source

Thrown at internal/application/repository/retriever/milvus/filter.go:132

		if childRes == nil || childRes.exprStr == "" {
			continue
		}
		if condResult == nil {
			condResult = childRes
			continue
		}

		condResult.exprStr = fmt.Sprintf(
			"(%s) %s (%s)",
			condResult.exprStr,
			strings.ToLower(cond.Operator),
			childRes.exprStr,
		)
		maps.Copy(condResult.params, childRes.params)
	}

	if condResult == nil {
		return nil, fmt.Errorf("empty logical condition")
	}
	return condResult, nil
}

func (c *filter) convertCondition(
	cond *universalFilterCondition,
	counter *int,
) (*convertResult, error) {
	if cond == nil {
		return nil, fmt.Errorf("milvus filter condition is nil")
	}
	switch cond.Operator {
	case operatorEqual, operatorNotEqual, operatorGreaterThan,
		operatorGreaterThanOrEqual, operatorLessThan,
		operatorLessThanOrEqual, operatorLike, operatorNotLike:
		return c.convertComparisonCondition(cond, counter)
	case operatorAnd, operatorOr:
		return c.convertLogicalCondition(cond, counter)

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Ensure AND/OR conditions contain at least one child condition in their Value array
  2. Validate the filter before calling Convert, rejecting logical operators with empty condition arrays
  3. Log the full condition tree to find the empty logical node

Example fix

// before
cond := &UniversalFilterCondition{Operator: "AND", Value: []*UniversalFilterCondition{}}
res, err := f.Convert(ctx, cond) // error: empty logical condition
// after
cond := &UniversalFilterCondition{Operator: "AND", Value: []*UniversalFilterCondition{
  {Field: "age", Operator: "GREATER_THAN", Value: 18},
}}
res, err := f.Convert(ctx, cond)
Defensive patterns

Strategy: validation

Validate before calling

func validLogical(c *UniversalFilterCondition) bool {
  if c.Operator != "AND" && c.Operator != "OR" { return true }
  conds, ok := c.Value.([]*UniversalFilterCondition)
  return ok && len(conds) > 0
}

Type guard

func isLogicalWithChildren(c *UniversalFilterCondition) bool {
  conds, ok := c.Value.([]*UniversalFilterCondition)
  return ok && len(conds) > 0
}

Try / catch

res, err := f.Convert(ctx, cond)
if err != nil {
  if strings.Contains(err.Error(), "empty logical condition") {
    return nil, fmt.Errorf("filter has an AND/OR node with no sub-conditions: %w", err)
  }
  return nil, err
}

Prevention

When it happens

Trigger: Calling Convert with a logical (AND/OR) condition whose Value array is empty, or whose children all fail silently to produce a result.

Common situations: Deserializing a filter from JSON where "value": [] for an AND/OR node; programmatically building nested filters and appending no sub-conditions.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/ebfaa6fe4acda383. Report an issue: GitHub.