usememos/memos · error

set operations require a list literal as the second argument

Error message

set operations require a list literal as the second argument

What it means

Set operations (in / not-in style membership over JSON list fields) require the second argument to be a CEL list literal, e.g. tag in ["work", "urgent"]. The parser inspects call.Args[1].GetListExpr(); when it is nil (an identifier, call, or string), compilation fails because only literal lists can be translated to SQL.

Source

Thrown at internal/filter/parser.go:726

	if len(call.Args) != 2 {
		return nil, errors.Errorf("%s expects two arguments", call.Function)
	}

	fieldName, err := getIdentName(call.Args[0])
	if err != nil {
		return nil, errors.Wrap(err, "set operations require a list field as the first argument")
	}
	field, ok := pc.schema.Field(fieldName)
	if !ok {
		return nil, errors.Errorf("unknown identifier %q", fieldName)
	}
	if field.Kind != FieldKindJSONList {
		return nil, errors.Errorf("set operations require a list field, got %q", fieldName)
	}

	listExpr := call.Args[1].GetListExpr()
	if listExpr == nil {
		return nil, errors.New("set operations require a list literal as the second argument")
	}
	values := make([]string, 0, len(listExpr.Elements))
	for _, el := range listExpr.Elements {
		v, err := getConstValue(el)
		if err != nil {
			return nil, errors.Wrap(err, "set operations only support literal string elements")
		}
		s, ok := v.(string)
		if !ok {
			return nil, errors.New("set operations require string elements")
		}
		values = append(values, s)
	}

	membership := func(s string) Condition {
		return &ElementInCondition{Element: &LiteralValue{Value: s}, Field: fieldName}
	}
	sizeEquals := func(n int) Condition {

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Wrap the values in a list literal: tag in ["work", "urgent"]
  2. For a single value, still use a one-element list: tag in ["work"]
  3. Build the list literal by joining pre-quoted strings when generating filters from code

Example fix

// before
tag in "work"

// after
tag in ["work"]
Defensive patterns

Strategy: validation

Validate before calling

// TS: build a safe membership filter from a string array
const buildIn = (field: string, values: string[]) =>
  `${field} in [${values.map(v => JSON.stringify(String(v))).join(', ')}]`;
if (values.length === 0) throw new Error('membership list cannot be empty');

Type guard

const isStringArray = (v: unknown): v is string[] => Array.isArray(v) && v.every(x => typeof x === 'string');

Prevention

When it happens

Trigger: tag in "work" (bare string instead of list), tag in tag (field on both sides), or tag in someList where someList is an unsupported function result.

Common situations: Treating in like SQL's IN with a comma string ("work,urgent"); attempting list-to-list containment between two fields; variable substitution that emits a non-list value.

Related errors


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/31305af924311d3e. Report an issue: GitHub.