A framework throws `InaccessibleObjectException` (or 'Unable to make field accessible') when reflecting into your module. What is the root cause and how do you fix it correctly?
answer
- Exception = reflection blocked because package not opened
- exports ≠ opens: exports won't fix reflection on private members
- Preferred fix: qualified `opens P to framework;`
- Can't edit module-info? use `--add-opens M/P=target` (ALL-UNNAMED for classpath)
- Error message literally names the missing opens directive
basics
~20 sThe framework is using reflection to reach into your code's private members, but your package is not opened. Modules block reflective access by default. Fix it by adding opens yourpackage; (or opens yourpackage to the.framework;) in module-info.java.
solid answer
~40 s`InaccessibleObjectException` means reflection tried to call `setAccessible(true)` on a member of a module-encapsulated package that was not opened. By default, JPMS blocks deep reflective access even into *exported* packages — exports grants normal access to public members, not reflection into private ones. The proper fix is a directive in your `module-info.java`: `opens com.example.model to com.fasterxml.jackson.databind;` (qualified to just the framework, keeping encapsulation tight), or `opens com.example.model;` to open it to all. If you can't edit module-info — e.g. a third-party module — you use the launcher flag `--add-opens com.example.app/com.example.model=com.framework` (or `ALL-UNNAMED` for the classpath). For a legacy module that is heavily reflected over, `open module com.example.app { ... }` opens every package. Prefer qualified `opens` over blanket flags so you don't silently undo strong encapsulation.
code
java · 10 lines// Fix in your own module:
module com.example.app {
requires com.fasterxml.jackson.databind;
opens com.example.app.model to com.fasterxml.jackson.databind; // tight, correct
}
// Can't edit module-info (3rd-party module)? launcher flag instead:
// java --add-opens com.example.app/com.example.app.model=com.fasterxml.jackson.databind ...
// Reflecting into JDK internals from the classpath:
// java --add-opens java.base/java.lang=ALL-UNNAMED ...go deeper
Knows the error means reflection is blocked and that opens is the fix.
Distinguishes exports from opens and adds a (qualified) opens directive correctly.
Reads the exception message to derive the exact directive, prefers qualified opens, and knows --add-opens/ALL-UNNAMED/MANIFEST escape hatches and why exports/public are wrong fixes.
Sets policy on opens scope across many modules and frameworks, controls --add-opens proliferation in build/runtime config, and weighs reflective-framework needs against strong encapsulation and JDK-internals restrictions.
## The error `java.lang.reflect.InaccessibleObjectException` (message often: *Unable to make field private ... accessible: module X does not "opens P" to module Y*) is thrown when reflective code calls `field.setAccessible(true)` (or `method`/`constructor` `setAccessible`) on a member that the **Java Platform Module System (JPMS)** has encapsulated. ## Why it happens — the default is closed A **module** strongly encapsulates its packages. JPMS distinguishes two kinds of access: - **Normal access** to *public* members of *exported* packages — controlled by `exports`. - **Deep reflective access** to *any* member (including `private`) at run time — controlled by `opens`. Key trap: even if a package is **exported**, that does NOT grant deep reflective access. Reflection that flips `setAccessible(true)` on a non-public member needs the package to be **opened**. By default packages are not opened, so frameworks that mutate private fields fail. Why do frameworks do this? JSON mappers (Jackson), ORMs (Hibernate/JPA), DI containers (Spring), and serializers routinely read and write **private** fields of your objects via reflection. Without `opens`, the JVM refuses, and you get `InaccessibleObjectException`. ## The correct fixes (in order of preference) ### 1. Qualified `opens` in your module-info If you own the module, open exactly the package to exactly the framework: ```java module com.example.app { requires com.fasterxml.jackson.databind; exports com.example.app.api; opens com.example.app.model to com.fasterxml.jackson.databind; } ``` This is the tightest, safest fix: only Jackson can reflect into `com.example.app.model`, and only that package is exposed. ### 2. Unqualified `opens` ```java opens com.example.app.model; ``` Opens the package to *all* modules for reflection. Use when you can't name the consumer (e.g., several frameworks) but still want package-level scope. ### 3. `open module` ```java open module com.example.app { ... } ``` Every package is opened for reflection. Convenient for legacy/heavily-reflected modules, but it discards package-level encapsulation, so use sparingly. ### 4. `--add-opens` launcher/compiler flag When you **cannot** edit the offending module's `module-info` (third-party code, or a quick migration): ``` --add-opens com.example.app/com.example.model=com.framework ``` For classpath (non-modular) code use `ALL-UNNAMED` as the target: ``` --add-opens java.base/java.lang=ALL-UNNAMED ``` Flags can also go in a JAR's `MANIFEST.MF` (`Add-Opens:`) or the `JDK_JAVA_OPTIONS` env var. Treat flags as an escape hatch, not a design choice — they undo encapsulation invisibly and must be repeated everywhere the app runs. ## What does NOT fix it - Adding `exports` — that grants normal access to public members, not reflection into private ones. - Making the field `public` — reflection on a public field of an *exported* package can work, but you usually don't want to break your data model's encapsulation just to satisfy a framework; `opens` is the intended tool. - Adding `requires` — readability doesn't grant reflective access. ## How to diagnose quickly The exception message names both modules and the missing directive: *module X does not "opens P" to module Y*. Read it literally: X needs `opens P to Y;` (or you supply `--add-opens X/P=Y`). ## Note on JDK internals Reflecting into JDK internal packages (e.g., `java.base/java.lang`) is increasingly restricted; since JDK 16 illegal reflective access is denied by default, and JDK 17+ has no relaxation flag for *its own* internals beyond explicit `--add-opens`. For your own modules, just use `opens`. ## One-line takeaway `InaccessibleObjectException` = reflection without `opens`. Fix by `opens <package> to <framework>;` in module-info (preferred), or `--add-opens` when you can't edit it — never by confusing it with `exports`.
- Why doesn't adding `exports` fix InaccessibleObjectException?Because `exports` only grants normal access to *public* members of a package. The framework is using reflection to touch *private* members (or to call `setAccessible(true)`), which is a separate capability controlled by `opens`. Reflection into encapsulated members needs the package opened, regardless of whether it is exported.
- When would you use `--add-opens` instead of editing module-info.java?When you can't change the module descriptor — typically a third-party/library module you don't own, or during a migration before you've added proper directives. For classpath (non-modular) code, target `ALL-UNNAMED`. It's an escape hatch: it must be applied at every launch and silently weakens encapsulation, so a qualified `opens` is preferred when you control the module.
Exporting a package is like unlocking the front door for guests. The framework needs to open the safe in the bedroom — that's opens. Unlocking the front door (exports) doesn't help; you must specifically open the safe.
saying these in an interview costs you the question
- Trying to fix it by adding `exports` instead of `opens`.
- Making fields public to satisfy a framework rather than using `opens`.
- Reaching for `open module` or broad `--add-opens java.base/...=ALL-UNNAMED` as a first resort instead of a qualified opens.
- Believing `requires` or readability grants reflective access.