cilium/cilium · warning

unexpected RHS expression length

Error message

unexpected RHS expression length

What it means

getEllipsisRHSExpansion requires the identifier's assigning statement to have exactly one RHS expression (i.e. a plain `x := expr`). Multi-value or tuple assignments such as `a, b := f()` have len(Rhs) != 1, so this error is returned. The analyzer cannot unambiguously attribute which RHS produced the spread slice.

Source

Thrown at tools/metricslint/pkg/analyzer/analyzer.go:54

func getEllipsisRHSExpansion(expr ast.Expr) (*ast.CompositeLit, error) {
	var ident *ast.Ident

	sel, ok := expr.(*ast.SelectorExpr)
	if ok {
		ident, ok = sel.X.(*ast.Ident)
	} else {
		ident, ok = expr.(*ast.Ident)
	}
	if !ok {
		return nil, fmt.Errorf("unknown identifier")
	}

	inlineAssignment, ok := ident.Obj.Decl.(*ast.AssignStmt)
	if !ok {
		return nil, fmt.Errorf("expected assignment statement")
	}
	if len(inlineAssignment.Rhs) != 1 {
		return nil, fmt.Errorf("unexpected RHS expression length")
	}

	slice, ok := inlineAssignment.Rhs[0].(*ast.CompositeLit)
	if !ok {
		return nil, fmt.Errorf("expected composite literal")
	}
	return slice, nil
}

func countArgs(call *ast.CallExpr) (int, error) {
	if call.Ellipsis == token.NoPos {
		// Simple path: Args are directly specified to the method.
		return len(call.Args), nil
	}
	if len(call.Args) != 1 {
		return 0, fmt.Errorf("unsupported ellipsis expression")
	}
	if warnDeprecated {

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Split the declaration so the slice gets its own single-value assignment: `vals := getVals()` rather than destructuring a multi-return.
  2. Reassign into a fresh single-value variable before the variadic call: `ks := keys; c.WithLabels(ks...)` still fails — instead build via `ks := []string{...}`.
  3. Suppress/ignore the analyzer report if the code is valid Go.
  4. Improve the tool to use go/types so tuple assignments resolve correctly.

Example fix

// before
// keys, extra := parseLabels(cfg)
// counter.WithLabels(keys...)
// after
// all := parseLabels(cfg)
// keys := all.keys
// counter.WithLabels(keys...)
Defensive patterns

Strategy: validation

Validate before calling

// Give the spread slice a single-RHS declaration:
// BAD: keys, extra := parseLabels(cfg)
// GOOD:
// parsed := parseLabels(cfg)
// keys := parsed.keys
func hasSingleRHS(as *ast.AssignStmt) bool { return len(as.Rhs) == 1 }

Type guard

func isSingleValueAssign(ident *ast.Ident) bool {
    as, ok := ident.Obj.Decl.(*ast.AssignStmt)
    return ok && len(as.Rhs) == 1 && len(as.Lhs) == 1
}

Try / catch

count, err := countArgs(call)
if err != nil && strings.Contains(err.Error(), "unexpected RHS expression length") {
    log.Printf("multi-value assignment not supported by metricslint; skipping")
    return
}

Prevention

When it happens

Trigger: Spread argument was declared in a multi-value assignment, e.g. `keys, vals := splitLabels()` then `c.WithLabels(keys...)`; also `sl, err := build()` followed by `WithLabels(sl...)`.

Common situations: Metric labels derived from a function returning two values; comma-ok type assertions (`v, ok := m["k"]`) used to build the slice.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/1ce66b58e21a3e96. Report an issue: GitHub.