alibaba/Sentinel · error · IllegalArgumentException

keys empty or null: ${keys}

Error message

keys empty or null: ${keys}

What it means

The List-based StatEntry constructor rejects a null or empty key list. StatEntry is one aggregated row in an EagleEye stat logger; its keys form the identity of the row, so an entry with zero keys cannot be written to the stat file. IllegalArgumentException is thrown at construction time, before any logging occurs.

Source

Thrown at sentinel-core/src/main/java/com/alibaba/csp/sentinel/eagleeye/StatEntry.java:83

    public StatEntry(StatLogger statLogger, String key1, String key2, String key3, String key4, String key5,
                     String key6, String key7, String key8) {
        this.statLogger = statLogger;
        this.keys = new String[] {key1, key2, key3, key4, key5, key6, key7, key8};
    }

    public StatEntry(StatLogger statLogger, String key1, String... moreKeys) {
        String[] keys = new String[1 + moreKeys.length];
        keys[0] = key1;
        for (int i = 0; i < moreKeys.length; ++i) {
            keys[i + 1] = moreKeys[i];
        }
        this.statLogger = statLogger;
        this.keys = keys;
    }

    public StatEntry(StatLogger statLogger, List<String> keys) {
        if (keys == null || keys.isEmpty()) {
            throw new IllegalArgumentException("keys empty or null: " + keys);
        }
        this.statLogger = statLogger;
        this.keys = keys.toArray(new String[keys.size()]);
    }

    public StatEntry(StatLogger statLogger, String[] keys) {
        if (keys == null || keys.length == 0) {
            throw new IllegalArgumentException("keys empty or null");
        }
        this.statLogger = statLogger;
        this.keys = Arrays.copyOf(keys, keys.length);
    }

    public String[] getKeys() {
        return keys;
    }

    void appendTo(StringBuilder appender, char delimiter) {

View on GitHub (pinned to a3f40ba8e9)

Solutions

  1. Check the list for null/empty before constructing the StatEntry and skip or default the entry.
  2. If the list is built by filtering, verify the filter predicate can actually match; log the input when the result is empty.
  3. Use the varargs constructor StatEntry(statLogger, key1, moreKeys...) when you always have at least one key.

Example fix

// before
StatEntry e = statLogger.stat(new ArrayList<String>()); // throws

// after
if (keys == null || keys.isEmpty()) {
    return; // or use a placeholder key
}
StatEntry e = statLogger.stat(keys);
Defensive patterns

Strategy: validation

Validate before calling

if (keys != null && !keys.isEmpty()) {
    StatEntry entry = statLogger.stat(keys);
} else {
    // skip or use default key
}

Prevention

When it happens

Trigger: Calling new StatEntry(statLogger, (List<String>) null) or passing an empty ArrayList (e.g. keys cleared earlier, or produced by a stream filter that removed everything) to the List constructor: if (keys == null || keys.isEmpty()) throw ... .

Common situations: Keys are built dynamically (request path, user ID, region) and under some traffic shapes the list ends up empty. A keys collection is populated conditionally and a code path skips all population. Refactoring from the varargs constructor to the List constructor exposes a previously silent empty case.

Related errors


AI-assisted analysis of alibaba/Sentinel@a3f40ba8e9 (2026-08-14). Data as JSON: /api/errors/eead0d6ed5ed2508. Report an issue: GitHub.