bazelbuild/bazel · error · RuntimeException

No filter expression specified after ${arg}

Error message

No filter expression specified after ${arg}

What it means

Thrown by JUnit4Options.parseArgs when a filter option that expects a separate value argument (e.g. --test_filter or --test_exclude_filter) is the last token on the command line. The parser sees the option name, calls it.next() to read the regular expression, and finds no more arguments, so it throws a RuntimeException rather than silently ignoring the filter.

Source

Thrown at src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/JUnit4Options.java:66

    Map<String, String> optionsMap = new HashMap<>();

    optionsMap.put(TEST_INCLUDE_FILTER_OPTION, null);
    optionsMap.put(TEST_EXCLUDE_FILTER_OPTION, null);

    for (Iterator<String> it = args.iterator(); it.hasNext();) {
      String arg = it.next();
      int indexOfEquals = arg.indexOf("=");

      if (indexOfEquals > 0) {
        String optionName = arg.substring(0, indexOfEquals);
        if (optionsMap.containsKey(optionName)) {
          optionsMap.put(optionName, arg.substring(indexOfEquals + 1));
          continue;
        }
      } else if (optionsMap.containsKey(arg)) {
        // next argument is the regexp
        if (!it.hasNext()) {
          throw new RuntimeException("No filter expression specified after " + arg);
        }
        optionsMap.put(arg, it.next());
        continue;
      }
      unparsedArgs.add(arg);
    }
    // If TESTBRIDGE_TEST_ONLY is set in the environment, forward it to the
    // --test_filter flag.
    String testFilter = envVars.get(TESTBRIDGE_TEST_ONLY);
    if (testFilter != null && optionsMap.get(TEST_INCLUDE_FILTER_OPTION) == null) {
      optionsMap.put(TEST_INCLUDE_FILTER_OPTION, testFilter);
    }
    boolean testRunnerFailFast = "1".equals(envVars.get(TESTBRIDGE_TEST_RUNNER_FAIL_FAST));
    return new JUnit4Options(
        testRunnerFailFast,
        optionsMap.get(TEST_INCLUDE_FILTER_OPTION),
        optionsMap.get(TEST_EXCLUDE_FILTER_OPTION),
        unparsedArgs.toArray(new String[0]));

View on GitHub (pinned to e6e199d060)

Solutions

  1. Use the equals form so the value is part of the token: --test_filter=com.foo.MyTest#method.
  2. If the value comes from a variable, guard it so the flag is only appended when the variable is non-empty.
  3. Pass the filter via the TESTBRIDGE_TEST_ONLY environment variable instead, which the runner forwards to --test_filter when no explicit filter is present.

Example fix

# before
java ... JUnit4Runner --test_filter $TESTS   # TESTS empty -> flag dangles
# after
FILTER=${TESTS:+--test_filter=$TESTS}
java ... JUnit4Runner $FILTER
Defensive patterns

Strategy: validation

Validate before calling

// before JUnit4Options.parseArgs
for (int i = 0; i < args.length; i++) {
  if (args[i].equals("--test_filter") || args[i].equals("--test_exclude_filter")) {
    if (i + 1 >= args.length || args[i + 1].startsWith("--")) {
      throw new IllegalArgumentException("Missing value for " + args[i]);
    }
  }
}

Try / catch

try {
  JUnit4Options.parseArgs(Arrays.asList(args), env);
} catch (RuntimeException e) {
  if (e.getMessage().startsWith("No filter expression")) { /* surface usage error */ }
}

Prevention

When it happens

Trigger: Invoking JUnit4Options.parseArgs(args, envVars) with args ending in "--test_filter" (or another space-separated option key) with no following value, e.g. args = ["--test_filter"].

Common situations: Shell scripts or bazel run invocations that build the filter from a variable which expands to empty at the end of the line (RUNNER_ARGS="--test_filter $FILTER" with FILTER unset); typo'd flag split across lines in CI config.

Related errors


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