{"record":{"id":"5430f11b278ec774","repo":"bazelbuild/bazel","slug":"documentation-level-is-no-longer-read-from-the-opt","errorCode":null,"errorMessage":"Documentation level is no longer read from the option category. Category \"%s\" is disallowed, see OptionMetadataTags for the relevant tags.","messagePattern":"Documentation level is no longer read from the option category\\. Category \"(.+?)\" is disallowed, see OptionMetadataTags for the relevant tags\\.","errorType":"validation","errorClass":"OptionProcessorException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java","lineNumber":424,"sourceCode":"      if (tag == OptionMetadataTag.HIDDEN || tag == OptionMetadataTag.INTERNAL) {\n        if (category != OptionDocumentationCategory.UNDOCUMENTED) {\n          throw new OptionProcessorException(\n              method,\n              \"Option has metadata tag %s but does not have category UNDOCUMENTED. Please fix.\",\n              tag);\n        }\n      }\n    }\n  }\n\n  private static final ImmutableSet<String> DEPRECATED_CATEGORIES =\n      ImmutableSet.of(\"undocumented\", \"hidden\", \"internal\");\n\n  private void checkOldCategoriesAreNotUsed(ExecutableElement method)\n      throws OptionProcessorException {\n    Option annotation = method.getAnnotation(Option.class);\n    if (DEPRECATED_CATEGORIES.contains(annotation.category())) {\n      throw new OptionProcessorException(\n          method,\n          \"Documentation level is no longer read from the option category. Category \\\"\"\n              + annotation.category()\n              + \"\\\" is disallowed, see OptionMetadataTags for the relevant tags.\");\n    }\n  }\n\n  private void checkExpansionOptions(ExecutableElement method) throws OptionProcessorException {\n    Option annotation = method.getAnnotation(Option.class);\n    boolean isExpansion = annotation.expansion().length > 0;\n    boolean hasImplicitRequirements = annotation.implicitRequirements().length > 0;\n\n    if (isExpansion && hasImplicitRequirements) {\n      throw new OptionProcessorException(\n          method,\n          \"Can't set an option to be both an expansion option and have implicit requirements.\");\n    }\n","sourceCodeStart":406,"sourceCodeEnd":442,"githubUrl":"https://github.com/bazelbuild/bazel/blob/e6e199d0601a244511b4cf18c8b2828aa73db1fd/src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java#L406-L442","documentation":"This compile-time error is produced by Bazel's options annotation processor. The old API encoded documentation level (undocumented/hidden/internal) as free-form strings in the option's category attribute; that mechanism was replaced by OptionMetadataTag (HIDDEN, INTERNAL, etc.) and OptionDocumentationCategory. The processor maintains an allowlist-free blocklist of the legacy category strings and rejects any option still using one.","triggerScenarios":"An @Option method whose category() attribute (or the @Option annotation's category parameter) is exactly \"undocumented\", \"hidden\", or \"internal\" — the strings in DEPRECATED_CATEGORIES.","commonSituations":"Upgrading Bazel or the options library to a version where the category-based scheme was removed; porting old options classes written against the pre-migration API; merging legacy code that predates OptionMetadataTag.","solutions":["Remove the category = \"...\" parameter from the @Option annotation.","Express documentation level via metadataTags: use OptionMetadataTag.HIDDEN or OptionMetadataTag.INTERNAL instead of the old category strings.","Set documentationCategory = OptionDocumentationCategory.UNDOCUMENTED for hidden/internal options (required by the related metadata check).","For all other options, choose an accurate OptionDocumentationCategory value describing where the flag belongs in generated docs."],"exampleFix":"// before\n@Option(\n  name = \"legacy_hidden_flag\",\n  defaultValue = \"true\",\n  category = \"hidden\",\n  effectTags = {OptionEffectTag.NO_OP}\n)\n// after\n@Option(\n  name = \"legacy_hidden_flag\",\n  defaultValue = \"true\",\n  documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,\n  effectTags = {OptionEffectTag.NO_OP},\n  metadataTags = {OptionMetadataTag.HIDDEN}\n)","handlingStrategy":"validation","validationCode":"// Reject legacy category strings before writing options code\nprivate static final Set<String> LEGACY = Set.of(\"undocumented\", \"hidden\", \"internal\");\n\nstatic String assertModernCategory(String category) {\n  Preconditions.checkArgument(!LEGACY.contains(category),\n      \"Legacy category '%s' removed; use OptionMetadataTag instead\", category);\n  return category;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Search options sources for category = \" at every upgrade of the options library.","New options should never set the category attribute at all — use documentationCategory and metadataTags."],"tags":["java","bazel","annotation-processing","options","compile-time","migration","deprecated-api"],"backgroundTag":null,"analyzedSha":"e6e199d0601a244511b4cf18c8b2828aa73db1fd","analyzedAt":"2026-08-14T10:24:27.848Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}