{"record":{"id":"d4039976052e75aa","repo":"google/guava","slug":"error-extracting-features-from-tester-annotation","errorCode":null,"errorMessage":"Error extracting features from tester annotation.","messagePattern":"Error extracting features from tester annotation\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"android/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java","lineNumber":190,"sourceCode":"  }\n\n  /**\n   * Find all the constraints explicitly or implicitly specified by a single tester annotation.\n   *\n   * @param testerAnnotation a tester annotation\n   * @return the requirements specified by the annotation\n   * @throws ConflictingRequirementsException if the requirements are mutually inconsistent.\n   */\n  private static TesterRequirements buildTesterRequirements(Annotation testerAnnotation)\n      throws ConflictingRequirementsException {\n    Class<? extends Annotation> annotationClass = testerAnnotation.annotationType();\n    Feature<?>[] presentFeatures;\n    Feature<?>[] absentFeatures;\n    try {\n      presentFeatures = (Feature<?>[]) annotationClass.getMethod(\"value\").invoke(testerAnnotation);\n      absentFeatures = (Feature<?>[]) annotationClass.getMethod(\"absent\").invoke(testerAnnotation);\n    } catch (Exception e) {\n      throw new IllegalArgumentException(\"Error extracting features from tester annotation.\", e);\n    }\n    Set<Feature<?>> allPresentFeatures = addImpliedFeatures(copyToSet(presentFeatures));\n    Set<Feature<?>> allAbsentFeatures = copyToSet(absentFeatures);\n    if (!disjoint(allPresentFeatures, allAbsentFeatures)) {\n      throw new ConflictingRequirementsException(\n          \"Annotation explicitly or \"\n              + \"implicitly requires one or more features to be both present \"\n              + \"and absent.\",\n          intersection(allPresentFeatures, allAbsentFeatures),\n          testerAnnotation);\n    }\n    return new TesterRequirements(allPresentFeatures, allAbsentFeatures);\n  }\n\n  /**\n   * Construct the set of requirements specified by annotations directly on a tester class or\n   * method.\n   *","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/google/guava/blob/94f39958baf7ad51ddf9c70e406ed6b188194daa/android/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java#L172-L208","documentation":"Thrown by FeatureUtil.buildTesterRequirements when reflective invocation of the annotation's value() or absent() methods fails. Tester annotations (@Feature.Require) must declare value() and absent() returning Feature[]; if either method is missing, throws, or returns an incompatible type, the catch-all wraps the cause in IllegalArgumentException.","triggerScenarios":"A custom @TesterAnnotation that omits value() or absent(), declares them with a non-Feature[] return type, or whose invocation throws (e.g. annotation type not really an annotation, or a proxy error). buildTesterRequirements is called while scanning tester class/method annotations.","commonSituations":"Authoring a custom tester annotation and forgetting to mirror the value()/absent() shape of the built-in @Require annotations; a stale/renamed Feature constant referenced by an annotation; classpath issues loading the annotation class.","solutions":["Ensure any custom @TesterAnnotation declares both Feature<?>[] value() default {} and Feature<?>[] absent() default {}.","Model custom annotations on the existing ones (CollectionFeature.Require, MapFeature.Require, CollectionSize.Require).","Inspect the wrapped cause (the IllegalArgumentException's getCause()) to see which method (value vs absent) failed and why."],"exampleFix":"// before\n@Retention(RUNTIME) @TesterAnnotation\npublic @interface MyFeatureRequire {\n  MyFeature[] value() default {};\n  // missing absent(); value() returns non-Feature type\n}\n\n// after\n@Retention(RUNTIME) @Inherited @TesterAnnotation\npublic @interface MyFeatureRequire {\n  Feature<?>[] value() default {};\n  Feature<?>[] absent() default {};\n}","handlingStrategy":"validation","validationCode":"// Custom tester annotation sanity check (run once at suite build time).\nClass<? extends Annotation> a = MyRequire.class;\nboolean ok = a.getMethod(\"value\").getReturnType().getComponentType() == Feature.class\n          && a.getMethod(\"absent\").getReturnType().getComponentType() == Feature.class;\nif (!ok) throw new IllegalStateException(\"@\" + a.getSimpleName() + \" must declare Feature<?>[] value() and absent()\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model custom tester annotations exactly on CollectionFeature.Require (value()+absent(), @TesterAnnotation, @Retention RUNTIME).","Add a startup self-test that validates every custom annotation's shape."],"tags":["guava-testlib","annotations","reflection","features"],"backgroundTag":null,"analyzedSha":"94f39958baf7ad51ddf9c70e406ed6b188194daa","analyzedAt":"2026-08-13T22:50:30.265Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}