{"record":{"id":"ef552d62204484fb","repo":"bazelbuild/bazel","slug":"option-does-not-list-at-least-one-optioneffecttag","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"validation","errorClass":"OptionProcessorException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java","lineNumber":376,"sourceCode":"    }\n\n    if (!ImmutableList.copyOf(annotation.metadataTags()).contains(OptionMetadataTag.INTERNAL)) {\n      if (!Pattern.matches(\"([\\\\w:-])*\", optionName)) {\n        // Ideally, this would be just \\w, but - and : are needed for legacy options. We can lie in\n        // the error though, no harm in encouraging good behavior.\n        throw new OptionProcessorException(\n            method,\n            \"Options that are used on the command line as flags must have names made from word \"\n                + \"characters only.\");\n      }\n    }\n  }\n\n  private void checkEffectTagRationality(ExecutableElement method) throws OptionProcessorException {\n    Option annotation = method.getAnnotation(Option.class);\n    OptionEffectTag[] effectTags = annotation.effectTags();\n    if (effectTags.length < 1) {\n      throw new OptionProcessorException(\n          method,\n          \"Option does not list at least one OptionEffectTag. If the option has no effect, \"\n              + \"please be explicit and add NO_OP. Otherwise, add a tag representing its effect.\");\n    } else if (effectTags.length > 1) {\n      // If there are more than 1 tag, make sure that NO_OP and UNKNOWN is not one of them.\n      // These don't make sense if other effects are listed.\n      ImmutableList<OptionEffectTag> tags = ImmutableList.copyOf(effectTags);\n      if (tags.contains(OptionEffectTag.UNKNOWN)) {\n        throw new OptionProcessorException(\n            method,\n            \"Option includes UNKNOWN with other, known, effects. Please remove UNKNOWN from \"\n                + \"the list.\");\n      }\n      if (tags.contains(OptionEffectTag.NO_OP)) {\n        throw new OptionProcessorException(\n            method,\n            \"Option includes NO_OP with other effects. This doesn't make much sense. Please \"\n                + \"remove NO_OP or the actual effects from the list, whichever is correct.\");","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/bazelbuild/bazel/blob/e6e199d0601a244511b4cf18c8b2828aa73db1fd/src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java#L358-L394","documentation":"Every @Option must declare at least one OptionEffectTag in its annotation — the machine-readable description of what setting the flag affects, used for option filtering and documentation. Zero tags fails this check; the message tells you to either declare a real effect or explicitly mark NO_OP when the flag truly has no effect.","triggerScenarios":"Writing @Option(name = \"x\", defaultValue = \"y\") with no effectTags array; adding a new flag quickly and skipping annotation metadata.","commonSituations":"New flag development; copying an old pre-annotation-mandate declaration; refactoring options where effectTags were dropped during merges.","solutions":["Pick accurate tags from OptionEffectTag (e.g. EAGERNESS_FOR_OUTPUT, EXECUTION_STRATEGY, LOADING_AND_ANALYSIS, UNKNOWN) matching the flag's real effect","If the flag genuinely does nothing (deprecated placeholder, marker), declare effectTags = {OptionEffectTag.NO_OP}","Note the sibling rule: NO_OP and UNKNOWN cannot be combined with other tags"],"exampleFix":"// before\n@Option(name = \"foo\", defaultValue = \"x\")\nString getFoo();\n// after\n@Option(name = \"foo\", defaultValue = \"x\", effectTags = {OptionEffectTag.NO_OP})\nString getFoo();","handlingStrategy":"validation","validationCode":"// Ensure every @Option declares at least one effect tag\nfor (var m : MyOptions.class.getMethods()) {\n  Option o = m.getAnnotation(Option.class);\n  if (o != null && o.effectTags().length == 0) {\n    throw new AssertionError(\"@Option \" + o.name() + \" needs at least one OptionEffectTag\");\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Choose the effect tag as part of designing the flag, not as an afterthought","Use NO_OP only for flags with no effect and never alongside other tags","Copy a fully-annotated existing @Option when adding new flags"],"tags":["bazel","options","annotation-processor","compile-time","metadata","java"],"backgroundTag":null,"analyzedSha":"e6e199d0601a244511b4cf18c8b2828aa73db1fd","analyzedAt":"2026-08-14T10:24:27.848Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}