bazelbuild/bazel · error · EvalException

expected at least one item

Error message

expected at least one item

What it means

Thrown by the built-in min()/max() when the iterable is empty. Guava's Ordering.max(Iterable) raises NoSuchElementException on an empty input; MethodLibrary converts it to EvalException with the message 'expected at least one item'. Starlark's min/max have no default= parameter, unlike Python's.

Source

Thrown at src/main/java/net/starlark/java/eval/MethodLibrary.java:141

      if (keyFn.isPresent()) {
        try {
          return stream(items)
              .map(value -> ValueWithComparisonKey.make(value, keyFn.get(), thread))
              .max(comparing(ValueWithComparisonKey::getComparisonKey, maxOrdering))
              .get()
              .getValue();
        } catch (ValueWithComparisonKey.KeyCallException ex) {
          Throwables.throwIfInstanceOf(ex.getCause(), EvalException.class);
          Throwables.throwIfInstanceOf(ex.getCause(), InterruptedException.class);
          throw new AssertionError("Got invalid ValueWithComparisonKey.KeyCallException", ex);
        }
      } else {
        return maxOrdering.max(items);
      }
    } catch (ClassCastException ex) {
      throw new EvalException(ex.getMessage()); // e.g. unsupported comparison: int <=> string
    } catch (NoSuchElementException ex) {
      throw new EvalException("expected at least one item", ex);
    } finally {
      EvalUtils.removeIterator(items);
    }
  }

  /**
   * Original value decorated with its comparison key; storing the comparison key alongside the
   * value ensures that we call the comparison key computation function only once per original value
   * (which is important in case the function has side effects).
   */
  private static final class ValueWithComparisonKey {
    private final Object value;
    private final Object comparisonKey;

    private ValueWithComparisonKey(Object value, Object comparisonKey) {
      this.value = value;
      this.comparisonKey = comparisonKey;
    }

View on GitHub (pinned to e6e199d060)

Solutions

  1. Guard the call: if len(items) > 0: m = max(items) else: <fallback>.
  2. Seed the iterable with a neutral element when the semantics allow it: max([0] + scores).
  3. Use a defaulting idiom: m = max(items) if items else None (then handle None downstream).

Example fix

# before
m = max([f for f in files if f.endswith('.cc')])

# after
cc = [f for f in files if f.endswith('.cc')]
m = max(cc) if cc else None
Defensive patterns

Strategy: validation

Validate before calling

if len(items) == 0:
    fail("cannot take max of empty list")
m = max(items)

Prevention

When it happens

Trigger: max([]), min(()) , max(dict()), or min/max over a comprehension/list that is empty at runtime (e.g. max([f for f in files if f.endswith('.cc')]) when no file matches).

Common situations: Glob or filter results that legitimately come back empty (no matching targets, no test files); optional attributes that default to an empty list; first iteration over generated input where the first batch is empty.

Related errors


AI-assisted analysis of bazelbuild/bazel@e6e199d060 (2026-08-14). Data as JSON: /api/errors/b302fda27395ce1a. Report an issue: GitHub.