bazelbuild/bazel · error · OptionsParsingException

'" + input + "' unexpected

Error message

'" + input + "' unexpected

What it means

Thrown by Converters.VoidConverter.convert when it receives any input other than null or the literal string "null". Void-typed options (typically expansion flags that only expand into other flags) carry no value, so their converter is never supposed to see real data; a non-empty value means the flag was used incorrectly.

Source

Thrown at src/main/java/com/google/devtools/common/options/Converters.java:194

    }

    @Override
    public String getTypeDescription() {
      return "a tri-state (auto, yes, no)";
    }
  }

  /**
   * Standard "converter" for Void. Should not actually be invoked. For instance, expansion flags
   * are usually Void-typed and do not invoke the converter.
   */
  public static class VoidConverter extends Converter.Contextless<Void> {
    @Override
    public Void convert(String input) throws OptionsParsingException {
      if (input == null || input.equals("null")) {
        return null; // expected input, return is unused so null is fine.
      }
      throw new OptionsParsingException("'" + input + "' unexpected");
    }

    @Override
    public String getTypeDescription() {
      return "";
    }
  }

  /** Standard converter for the {@link java.time.Duration} type. */
  public static class DurationConverter extends Converter.Contextless<Duration> {
    private static final Pattern DURATION_REGEX = Pattern.compile("^([0-9]+)(d|h|m|s|ms|ns)$");

    @Override
    public Duration convert(String input) throws OptionsParsingException {
      // To be compatible with the previous parser, '0' doesn't need a unit.
      if ("0".equals(input)) {
        return Duration.ZERO;
      }

View on GitHub (pinned to e6e199d060)

Solutions

  1. Pass the flag bare with no value: --my_expansion_flag.
  2. Check the flag's documentation/type description (empty string means no value expected).
  3. If you need a parameterized variant, it must be a separate flag with a real converter, not the Void-typed expansion flag.

Example fix

# before
bazel build --experimental_expansion_flag=true //...
# after
bazel build --experimental_expansion_flag //...
Defensive patterns

Strategy: validation

Validate before calling

// Void/expansion flags take no value: reject before parsing
for (String arg : args) {
  if (VOID_FLAG_NAMES.stream().anyMatch(arg::startsWith)
      && (arg.contains("=") && !arg.endsWith("=null"))) {
    throw new IllegalArgumentException("Flag takes no value: " + arg);
  }
}

Type guard

boolean isBareVoidFlag(String arg, Set<String> voidFlags) {
  return voidFlags.contains(arg); // exactly the bare name, no '=' and no value token
}

Prevention

When it happens

Trigger: Passing --my_expansion_flag=anything (equals form with a value) or --my_expansion_flag value (space form) where the option is annotated with converter = VoidConverter.class.

Common situations: Users adding =true to an expansion/alias flag out of habit; tools that always emit --flag=value syntax regardless of the flag type; copy-pasting from docs of a different flag that does take a value.

Related errors


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