bazelbuild/bazel · error · OptionProcessorException

Options that are used on the command line as flags must have

Error message

Options that are used on the command line as flags must have names made from word characters only.

What it means

Flag names are used verbatim on the command line, so (unless the option is tagged OptionMetadataTag.INTERNAL) the processor validates the name against the pattern [\w:-]* — word characters plus, as legacy tolerance, '-' and ':'. Any other character (space, '=', '.', '/', '#', etc.) is rejected with this message, which deliberately overstates the restriction to push toward clean names.

Source

Thrown at src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java:364

    checkConverter(method);
    checkEffectTagRationality(method);
    checkMetadataTagAndCategoryRationality(method);
    checkNoDefaultValueForMultipleOption(method);
    checkDeprecated(method);
  }

  private void checkOptionName(ExecutableElement method) throws OptionProcessorException {
    Option annotation = method.getAnnotation(Option.class);
    String optionName = annotation.name();
    if (optionName.isEmpty()) {
      throw new OptionProcessorException(method, "Option must have an actual name.");
    }

    if (!ImmutableList.copyOf(annotation.metadataTags()).contains(OptionMetadataTag.INTERNAL)) {
      if (!Pattern.matches("([\\w:-])*", optionName)) {
        // Ideally, this would be just \w, but - and : are needed for legacy options. We can lie in
        // the error though, no harm in encouraging good behavior.
        throw new OptionProcessorException(
            method,
            "Options that are used on the command line as flags must have names made from word "
                + "characters only.");
      }
    }
  }

  private void checkEffectTagRationality(ExecutableElement method) throws OptionProcessorException {
    Option annotation = method.getAnnotation(Option.class);
    OptionEffectTag[] effectTags = annotation.effectTags();
    if (effectTags.length < 1) {
      throw new OptionProcessorException(
          method,
          "Option does not list at least one OptionEffectTag. If the option has no effect, "
              + "please be explicit and add NO_OP. Otherwise, add a tag representing its effect.");
    } else if (effectTags.length > 1) {
      // If there are more than 1 tag, make sure that NO_OP and UNKNOWN is not one of them.
      // These don't make sense if other effects are listed.

View on GitHub (pinned to e6e199d060)

Solutions

  1. Rewrite the name using only letters, digits, underscore, and (if legacy-needed) '-' or ':'
  2. If a weird name is genuinely required and never user-facing, add metadataTags = {OptionMetadataTag.INTERNAL}
  3. Prefer snake_case word characters for new flags since '-' and ':' are legacy allowances

Example fix

// before
@Option(name = "foo.bar", effectTags = {OptionEffectTag.NO_OP})
String getFooBar();
// after
@Option(name = "foo_bar", effectTags = {OptionEffectTag.NO_OP})
String getFooBar();
Defensive patterns

Strategy: validation

Validate before calling

// Validate the option name charset ([\w:-]*) outside the processor
var OK = java.util.regex.Pattern.compile("[\\w:-]*");
for (var m : MyOptions.class.getMethods()) {
  Option o = m.getAnnotation(Option.class);
  if (o != null && !o.name().isEmpty() && !OK.matcher(o.name()).matches()
      && !java.util.List.of(o.metadataTags()).contains(OptionMetadataTag.INTERNAL)) {
    throw new AssertionError("Illegal option name: " + o.name());
  }
}

Prevention

When it happens

Trigger: Declaring @Option(name = "foo.bar"), "my flag", "foo=value", or any name with non-[\w:-] characters and not marking it INTERNAL.

Common situations: Wanting dotted or namespaced flag names; pasting names from external tooling with spaces or unicode; forgetting that '=' in a name would make '--name=value' ambiguous.

Related errors


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