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
- Pass the flag bare with no value: --my_expansion_flag.
- Check the flag's documentation/type description (empty string means no value expected).
- 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
- Learn which flags are expansion flags from --help; pass them bare.
- Do not template flags as --flag=value unconditionally in generated command lines.
- Treat "'X' unexpected" from VoidConverter as a usage bug in the caller, not a data problem.
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
- '" + input + "' is not a boolean
- '" + input + "' is not an int
- '" + input + "' is not a long
- '" + input + "' is not a double
- Not a valid %s: '%s' (should be auto or a boolean)
AI-assisted analysis of bazelbuild/bazel@e6e199d060 (2026-08-14).
Data as JSON: /api/errors/57ad88697cb9692f.
Report an issue: GitHub.