Files
IMR6/libraries/linux/graalvm/lib/svm/skills/building-native-image/references/reachability-metadata.md
T

10 KiB

Reachability Metadata GraalVM Native Image

Table of Contents

  1. Diagnosing the Error Type
  2. Where to Put Metadata Files
  3. Reflection Metadata
  4. JNI Metadata
  5. Resource Metadata
  6. Serialization Metadata
  7. Conditional Metadata Entries
  8. Debugging Tips
  9. Full Sample reachability-metadata.json

1. Diagnosing the Error Type

Match the runtime error to the metadata section you need to fix:

Runtime Error Root Cause Fix In Section
NoClassDefFoundError Class not included in binary Reflection Metadata - register the type
MissingReflectionRegistrationError Reflective access to unregistered class/method/field Reflection Metadata
NoSuchMethodException Method not registered for reflective invocation Reflection Metadata - Methods
NoSuchFieldException Field not registered for reflective access Reflection Metadata - Fields
MissingJNIRegistrationError JNI lookup of unregistered type/member JNI Metadata
MissingForeignRegistrationError FFM downcall/upcall without registered descriptor Foreign section (advanced, see GraalVM docs)
MissingResourceException Resource bundle not included Resource Metadata - Bundles

Quick diagnostic command - run the app with warning mode to see all missing registrations without crashing:

java -XX:MissingRegistrationReportingMode=Warn -jar your-app.jar

Use Exit mode during testing to catch errors hidden inside catch (Throwable t) blocks:

java -XX:MissingRegistrationReportingMode=Exit -jar your-app.jar

Enable strict metadata mode at build time:

native-image --exact-reachability-metadata ...
# Or for specific packages only:
native-image --exact-reachability-metadata=com.example.mypackage ...

2. Where to Put Metadata Files

All metadata lives in a single JSON file on the classpath:

src/main/resources/
└── META-INF/
    └── native-image/
        └── <groupId>/
            └── <artifactId>/
                └── reachability-metadata.json

The file contains a top-level object with one key per metadata type:

{
  "reflection": [],
  "resources": []
}

Alternative approaches (when JSON isn't enough):

  • Pass constant arguments to Class.forName("Foo"), getMethod(...), etc. - native-image evaluates these at build time automatically.
  • Use -H:Preserve=<package> to preserve entire packages.

3. Reflection Metadata

Register a Type (fixes NoClassDefFoundError, MissingReflectionRegistrationError)

{
  "reflection": [
    {
      "type": "com.example.MyClass"
    }
  ]
}

This allows Class.forName("com.example.MyClass") and reflective lookups to find the type.

Methods

Fixes NoSuchMethodError and MissingReflectionRegistrationError on Method.invoke() or Constructor.newInstance().

Register specific methods:

{
  "type": "com.example.MyClass",
  "methods": [
    { "name": "myMethod", "parameterTypes": ["java.lang.String", "int"] },
    { "name": "<init>", "parameterTypes": [] }
  ]
}

Use "<init>" for constructors.

Register all methods (less precise, larger binary):

{
  "type": "com.example.MyClass",
  "allDeclaredMethods": true,
  "allPublicMethods": true,
  "allDeclaredConstructors": true,
  "allPublicConstructors": true
}
  • allDeclared* - methods/constructors declared directly on this type
  • allPublic* - all public methods/constructors including those inherited from supertypes

Fields

Fixes NoSuchFieldException and MissingReflectionRegistrationError on Field.get() / Field.set().

Register specific fields:

{
  "type": "com.example.MyClass",
  "fields": [
    { "name": "myField" },
    { "name": "anotherField" }
  ]
}

Register all fields:

{
  "type": "com.example.MyClass",
  "allDeclaredFields": true,
  "allPublicFields": true
}

Dynamic Proxies

For classes obtained via Proxy.newProxyInstance(...) - the type is the proxy's interface list:

{
  "type": {
    "proxy": ["com.example.IFoo", "com.example.IBar"]
  }
}

The interface order matters - it must match the order passed to Proxy.newProxyInstance.

Unsafe Allocation

For Unsafe.allocateInstance(MyClass.class):

{
  "type": "com.example.MyClass",
  "unsafeAllocated": true
}

Full Type Entry Reference

{
  "condition": { "typeReached": "com.example.TriggerClass" },
  "type": "com.example.MyClass",
  "fields": [{ "name": "fieldName" }],
  "methods": [{ "name": "methodName", "parameterTypes": ["java.lang.String"] }],
  "allDeclaredConstructors": true,
  "allPublicConstructors": true,
  "allDeclaredMethods": true,
  "allPublicMethods": true,
  "allDeclaredFields": true,
  "allPublicFields": true,
  "unsafeAllocated": true,
  "serializable": true
}

4. JNI Metadata

Used when native C/C++ code calls back into Java via JNI. Fixes MissingJNIRegistrationError.

Most JNI libraries don't handle Java exceptions gracefully - always use --exact-reachability-metadata with -XX:MissingRegistrationReportingMode=Warn to see what's missing.

Register a JNI-accessible type:

{
  "reflection": [
    {
      "type": "com.example.MyClass",
      "jniAccessible": true
    }
  ]
}

Add fields and methods for JNI access:

{
  "type": "com.example.MyClass",
  "jniAccessible": true,
  "fields": [{ "name": "value" }],
  "methods": [
    { "name": "callback", "parameterTypes": ["int"] }
  ],
  "allDeclaredConstructors": true
}

JNI metadata follows the same allDeclared* / allPublic* convenience flags as reflection.


5. Resource Metadata

Embed Resources (fixes missing getResourceAsStream results)

Resources are specified using glob patterns in the resources array:

{
  "resources": [
    { "glob": "config/app.properties" },
    { "glob": "templates/**" },
    { "glob": "**/Resource*.txt" }
  ]
}

Glob rules:

  • * matches any characters on one path level
  • ** matches any characters across multiple levels
  • No trailing slash, no empty levels, no ***

Examples:

{ "glob": "config/app.properties" }        // exact file
{ "glob": "**/**.json" }                   // all JSON files anywhere
{ "glob": "static/images/*.png" }          // all PNGs in one directory

Note: Class.getResourceAsStream("plan.txt") with a class literal and string literal is auto-detected by native-image - no JSON needed for those cases.

Resources from a Specific Module

{
  "resources": [
    {
      "module": "library.module",
      "glob": "resource-file.txt"
    }
  ]
}

Resource Bundles

Fixes MissingResourceException from ResourceBundle.getBundle(...).

{
  "resources": [
    { "bundle": "com.example.Messages" },
    { "bundle": "com.example.Errors" }
  ]
}

With a specific module:

{
  "resources": [
    { "module": "app.module", "bundle": "com.example.Messages" }
  ]
}

Bundles are included for all locales embedded in the image. To control locales:

native-image -Duser.country=US -Duser.language=en -H:IncludeLocales=fr,de
# or include everything (significantly increases image size):
native-image -H:+IncludeAllLocales

6. Serialization Metadata

Fixes InvalidClassException, serialization StreamCorruptedException, or ClassNotFoundException during ObjectInputStream.readObject().

In JSON

{
  "reflection": [
    {
      "type": "com.example.MySerializableClass",
      "serializable": true
    }
  ]
}

Via Code (auto-detected)

If you use ObjectInputFilter, native-image detects this automatically when the pattern is a constant:

var filter = ObjectInputFilter.Config.createFilter("com.example.MyClass;!*;");
objectInputStream.setObjectInputFilter(filter);

Proxy Serialization

{
  "reflection": [
    {
      "type": {
        "proxy": ["com.example.IFoo"],
        "serializable": true
      }
    }
  ]
}

7. Conditional Metadata Entries

Use conditions to avoid bloating the binary with metadata for code paths that may never run.

{
  "condition": {
    "typeReached": "com.example.FeatureModule"
  },
  "type": "com.example.OptionalClass",
  "allDeclaredMethods": true
}

The metadata for OptionalClass is only active at runtime once FeatureModule has been initialized. It is still included at build time if FeatureModule is reachable during static analysis.

A type is "reached" right before its static initializer runs, or when any of its subtypes are reached.

Use conditions liberally on third-party library metadata to keep binary size reasonable.


9. Full Sample reachability-metadata.json

{
  "reflection": [
    {
      "condition": { "typeReached": "com.example.App" },
      "type": "com.example.MyClass",
      "fields": [
        { "name": "myField" }
      ],
      "methods": [
        { "name": "myMethod", "parameterTypes": ["java.lang.String"] },
        { "name": "<init>", "parameterTypes": [] }
      ],
      "allDeclaredConstructors": true,
      "allPublicConstructors": true,
      "allDeclaredFields": true,
      "allPublicFields": true,
      "allDeclaredMethods": true,
      "allPublicMethods": true,
      "unsafeAllocated": true,
      "serializable": true
    },
    {
      "type": {
        "proxy": ["com.example.IFoo", "com.example.IBar"]
      }
    },
    {
      "type": "com.example.JniClass",
      "jniAccessible": true,
      "fields": [{ "name": "nativeHandle" }],
      "allDeclaredMethods": true
    }
  ],
  "resources": [
    {
      "glob": "config/**"
    },
    {
      "module": "app.module",
      "glob": "static/index.html"
    },
    {
      "bundle": "com.example.Messages"
    }
  ]
}