Tencent/WeKnora · error

between operator value must be a slice with two elements: %v

Error message

between operator value must be a slice with two elements: %v

What it means

convertBetweenCondition requires Value to be a slice of exactly two elements (lower and upper bound). It uses reflection and returns this error when Value is not a slice or its length != 2, embedding Value in the message.

Source

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

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

func (c *filter) convertBetweenCondition(
	cond *universalFilterCondition,
	counter *int,
) (*convertResult, error) {
	condField := cond.Field
	if condField == "" || cond.Value == nil {
		return nil, fmt.Errorf("milvus filter condition is nil")
	}

	value := reflect.ValueOf(cond.Value)
	if value.Kind() != reflect.Slice || value.Len() != 2 {
		return nil, fmt.Errorf("between operator value must be a slice with two elements: %v", cond.Value)
	}

	paramBase := c.convertParamName(cond.Field, counter)
	paramName1 := fmt.Sprintf("%s_%d", paramBase, 0)
	paramName2 := fmt.Sprintf("%s_%d", paramBase, 1)
	return &convertResult{
		exprStr: fmt.Sprintf("%s >= {%s} and %s <= {%s}", condField, paramName1, condField, paramName2),
		params: map[string]any{
			paramName1: value.Index(0).Interface(),
			paramName2: value.Index(1).Interface(),
		},
	}, nil
}

func formatValue(value any) string {
	switch v := value.(type) {
	case string:
		return fmt.Sprintf("\"%s\"", escapeDoubleQuotes(v))

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Always pass a two-element slice: [min, max]
  2. Use GREATER_THAN_OR_EQUAL / LESS_THAN_OR_EQUAL pair for open-ended ranges
  3. Check array construction so exactly two bounds are provided

Example fix

// before
cond := &UniversalFilterCondition{Field: "age", Operator: "BETWEEN", Value: []int{18}}
// after
cond := &UniversalFilterCondition{Field: "age", Operator: "BETWEEN", Value: []int{18, 65}}
Defensive patterns

Strategy: validation

Validate before calling

rv := reflect.ValueOf(cond.Value)
if rv.Kind() != reflect.Slice || rv.Len() != 2 {
  return fmt.Errorf("BETWEEN requires exactly [min, max]")
}

Type guard

func isTwoElementSlice(c *UniversalFilterCondition) bool {
  if c.Value == nil { return false }
  v := reflect.ValueOf(c.Value)
  return v.Kind() == reflect.Slice && v.Len() == 2
}

Try / catch

res, err := f.Convert(ctx, cond)
if err != nil {
  if strings.Contains(err.Error(), "two elements") {
    return nil, fmt.Errorf("BETWEEN value must be [min,max]: %w", err)
  }
  return nil, err
}

Prevention

When it happens

Trigger: BETWEEN with a scalar value, a one-element slice, or a three-plus-element slice.

Common situations: Passing [lower] intending an open-ended range; passing [lower, upper, step]; mixing up BETWEEN with IN semantics.

Related errors


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