cilium/cilium · warning

expected non-zero array length

Error message

expected non-zero array length

What it means

When the spread slice's type is not an *ast.ArrayType (so it's not a simple static array), the analyzer expects the literal to contain exactly one KeyValueExpr element describing a nested map/slice initializer. If the composite literal has zero or multiple elements, this inner 'expected non-zero array length' error is created and wrapped as 'unsupported nested varlen array'.

Source

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

	if len(call.Args) != 1 {
		return 0, fmt.Errorf("unsupported ellipsis expression")
	}
	if warnDeprecated {
		fmt.Fprintf(os.Stderr, "metricslint: Warning: Using deprecated 'ast.Object'\n")
		warnDeprecated = false
	}

	slice, err := getEllipsisRHSExpansion(call.Args[0])
	if slice == nil {
		return 0, fmt.Errorf("unsupported varlen array: %w", err)
	}
	if _, ok := slice.Type.(*ast.ArrayType); ok {
		// Ellipsis points to a static array, so we can count the
		// number of parameters to the method following expansion.
		return len(slice.Elts), nil
	}
	if len(slice.Elts) != 1 {
		err := fmt.Errorf("expected non-zero array length")
		return 0, fmt.Errorf("unsupported nested varlen array: %w", err)
	}

	nestedKV, ok := slice.Elts[0].(*ast.KeyValueExpr)
	if !ok {
		return 0, fmt.Errorf("unsupported nested varlen array type")
	}
	nestedSlice, err := getEllipsisRHSExpansion(nestedKV.Value)
	if nestedSlice == nil {
		return 0, fmt.Errorf("unsupported nested varlen array: %w", err)
	}
	return len(nestedSlice.Elts), nil
}

func filterRelevantConstructors(node ast.Node) (object, constructor string, argCount int, err error) {
	// Look for an initializer with key-value expressions that call another
	// function to initialize the field.
	kv, ok := node.(*ast.KeyValueExpr)

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Ensure the literal has exactly one element in the nested form the analyzer expects: `m := map[string][]string{"k": {"a", "b"}}`.
  2. For static arrays, declare with an explicit ArrayType so the earlier `len(slice.Elts)` path is used instead.
  3. Avoid passing empty literals with '...'; provide at least one element or restructure.
  4. Suppress the analyzer report if the code is valid; this is a tool limitation.
  5. Extend countArgs to handle multi-element non-array literals via go/types.

Example fix

// before
// labels := map[string][]string{}
// counter.WithLabels(labels["k"]...)
// after
// labels := map[string][]string{"k": {"src", "dst"}}
// counter.WithLabels(labels["k"]...)
Defensive patterns

Strategy: validation

Validate before calling

// For nested (non-array) literals, keep exactly one KeyValueExpr element:
// m := map[string][]string{"k": {"a", "b"}}
// counter.WithLabels(m["k"]...)
func singleKVLiteral(lit *ast.CompositeLit) bool {
    if _, isArray := lit.Type.(*ast.ArrayType); isArray {
        return true
    }
    return len(lit.Elts) == 1
}

Type guard

func nestedShapeSupported(lit *ast.CompositeLit) bool {
    if _, ok := lit.Type.(*ast.ArrayType); ok {
        return true
    }
    if len(lit.Elts) != 1 {
        return false
    }
    _, ok := lit.Elts[0].(*ast.KeyValueExpr)
    return ok
}

Try / catch

if len(slice.Elts) != 1 {
    log.Printf("literal has %d elements; analyzer only supports 1 nested entry", len(slice.Elts))
    return
}

Prevention

When it happens

Trigger: Spread argument is a composite literal whose Type is not a plain ArrayType and whose Elts length != 1, e.g. an empty literal []string{} or a literal with two+ non-keyed elements reaching the nested-expansion branch.

Common situations: Empty label slices passed with '...' in one branch of the code; map-typed literals (map[string][]string{...}) with several entries; hand-written nested initializers the analyzer didn't anticipate.

Related errors


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