{"record":{"id":"4b2bd3b65bdd5d0e","repo":"apache/beam","slug":"class-has-no-path-back-to-any-root-class-it-should-never","errorCode":null,"errorMessage":"Class  has no path back to any root class. It should never have been considered exposed.","messagePattern":"Class  has no path back to any root class\\. It should never have been considered exposed\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/util/ApiSurface.java","lineNumber":406,"sourceCode":"  }\n\n  /** Returns exposed types in this set, including arrays and primitives as specified. */\n  public Set<Class<?>> getExposedClasses() {\n    return getExposedToExposers().keySet();\n  }\n\n  /**\n   * Returns a path from an exposed class to a root class. There may be many, but this gives only\n   * one.\n   *\n   * <p>If there are only cycles, with no path back to a root class, throws IllegalStateException.\n   */\n  public List<Class<?>> getAnyExposurePath(Class<?> exposedClass) {\n    Set<Class<?>> excluded = Sets.newHashSet();\n    excluded.add(exposedClass);\n    List<Class<?>> path = getAnyExposurePath(exposedClass, excluded);\n    if (path == null) {\n      throw new IllegalArgumentException(\n          \"Class \"\n              + exposedClass\n              + \" has no path back to any root class.\"\n              + \" It should never have been considered exposed.\");\n    } else {\n      return path;\n    }\n  }\n\n  /**\n   * Returns a path from an exposed class to a root class. There may be many, but this gives only\n   * one. It will not return a path that crosses the excluded classes.\n   *\n   * <p>If there are only cycles or paths through the excluded classes, returns null.\n   *\n   * <p>If the class is not actually in the exposure map, throws IllegalArgumentException\n   */\n  private List<Class<?>> getAnyExposurePath(Class<?> exposedClass, Set<Class<?>> excluded) {","sourceCodeStart":388,"sourceCodeEnd":424,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/util/ApiSurface.java#L388-L424","documentation":"ApiSurface tracks classes exposed from a set of root classes and records an exposure path from each exposed class back to a root. getAnyExposurePath(exposedClass) is an internal consistency check: if a class is in the exposed set but no path to any root exists, the index is corrupt, so it throws IllegalArgumentException. This is a library-internal invariant violation, not something caused by user data.","triggerScenarios":"Calling getAnyExposurePath on a class that is not in the exposed set, or on an ApiSurface whose exposure graph was built without any matching root classes; programmatic use of ApiSurface where the query class was never added via exposedClass/exposure roots.","commonSituations":"Custom API-surface checks or test utilities built on org.apache.beam.sdk.util.ApiSurface querying classes excluded from the surface; root-class configuration missing the package containing the queried class.","solutions":["Only call getAnyExposurePath on classes returned by the surface's exposedClasses()/contains logic — verify membership first.","Ensure the root classes supplied when building the ApiSurface actually cover the package of the class being queried.","Wrap the call in try-catch for IllegalArgumentException if you are probing arbitrary classes, and fall back to reporting 'not exposed'."],"exampleFix":"// before\nList<Class<?>> path = apiSurface.getAnyExposurePath(SomeClass.class);\n// after\nif (apiSurface.getExposedClasses().contains(SomeClass.class)) {\n  List<Class<?>> path = apiSurface.getAnyExposurePath(SomeClass.class);\n} else {\n  // class is not part of the tracked surface\n}","handlingStrategy":"try-catch","validationCode":"if (!surface.getExposedClasses().contains(clazz)) { /* skip */ }","typeGuard":null,"tryCatchPattern":"try {\n  List<Class<?>> path = surface.getAnyExposurePath(clazz);\n} catch (IllegalArgumentException e) {\n  // treat as 'not tracked in this surface'\n}","preventionTips":["Query exposure paths only for classes known to be in the surface.","Keep root-class configuration in sync with the packages you analyze.","Build ApiSurface instances once per analysis and reuse; don't query across instances."],"tags":["java","apache-beam","api-surface","internal-state"],"backgroundTag":"internal-invariant-violation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}