Tencent/WeKnora · error

invalid logical condition value type

Error message

invalid logical condition value type

What it means

convertLogicalCondition type-asserts cond.Value to []*universalFilterCondition to obtain the child conditions of a logical node. If Value is non-nil but holds any other type, it returns 'invalid logical condition value type'. This protects the converter from malformed filter trees where a logical node's payload was built with the wrong element type.

Source

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

	}

	paramName := c.convertParamName(cond.Field, counter)
	return &convertResult{
		exprStr: fmt.Sprintf("%s %s {%s}", condField, operator, paramName),
		params:  map[string]any{paramName: cond.Value},
	}, nil
}

func (c *filter) convertLogicalCondition(
	cond *universalFilterCondition,
	counter *int,
) (*convertResult, error) {
	if cond.Value == nil {
		return nil, fmt.Errorf("milvus filter condition is nil")
	}
	conds, ok := cond.Value.([]*universalFilterCondition)
	if !ok {
		return nil, fmt.Errorf("invalid logical condition value type")
	}

	var condResult *convertResult
	for _, childCond := range conds {
		childRes, err := c.convertCondition(childCond, counter)
		if err != nil {
			return nil, err
		}
		if childRes == nil || childRes.exprStr == "" {
			continue
		}
		if condResult == nil {
			condResult = childRes
			continue
		}

		condResult.exprStr = fmt.Sprintf(
			"(%s) %s (%s)",

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Ensure logical conditions are constructed only via the helper that stores []*universalFilterCondition in Value.
  2. Fix the caller constructing the tree so children are appended as *universalFilterCondition pointers.
  3. If input comes from JSON, decode into the typed condition structs first, then build the tree; never pass raw decoded values.
  4. Add a constructor (e.g. newLogicalCond(op string, children []*universalFilterCondition)) and forbid direct Value assignment.

Example fix

// before
node := &universalFilterCondition{Operator: "AND", Value: children} // children is []universalFilterCondition
// after
ptrs := make([]*universalFilterCondition, len(children))
for i := range children {
	ptrs[i] = &children[i]
}
node := &universalFilterCondition{Operator: "AND", Value: ptrs}
Defensive patterns

Strategy: type-guard

Validate before calling

if kids, ok := cond.Value.([]*universalFilterCondition); !ok || len(kids) == 0 {
	return errors.New("logical condition must carry []*universalFilterCondition children")
}

Type guard

func asChildConditions(v any) ([]*universalFilterCondition, bool) {
	kids, ok := v.([]*universalFilterCondition)
	return kids, ok && len(kids) > 0
}

Prevention

When it happens

Trigger: Building a logical condition whose Value is []universalFilterCondition (no pointer), []*someOtherCondType, a single *universalFilterCondition, or a raw map/slice from JSON decoding — any non-[]*universalFilterCondition payload.

Common situations: JSON/YAML filter decoding producing map[string]any instead of typed conditions; refactors changing the condition struct without updating group builders; mixing condition types from two different filter packages.

Related errors


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