java-native-access/jna · error · java.lang.IllegalArgumentException
OPTIONS must be a public field of type java.util.Map (e)…
Error message
OPTIONS must be a public field of type java.util.Map (e): mappingClass
What it means
When JNA initializes a library mapping it reads the optional static OPTIONS field of the library interface via Class.getField and a cast to Map<String,Object>. If the field exists but is not public/static, not of type Map, is non-null-inaccessible, or throws during access, JNA wraps the failure in this IllegalArgumentException. It means the developer declared an OPTIONS field that does not match the contract documented in Library.
Solutions
- Change the OPTIONS field to be public static final java.util.Map<String,Object>, e.g. Map OPTIONS = new HashMap();
- If no options are needed, delete the field entirely (JNA treats a missing OPTIONS as empty options)
- Ensure any static initializer populating OPTIONS cannot throw
- Verify the field is not shadowed with a wrong type in a subclass/nested interface
Example fix
// before
private static String OPTIONS = "advanced";
// after
public static final Map<String, Object> OPTIONS = new HashMap<String, Object>() {{ put(Library.OPTION_CALLING_CONVENTION, Function.C_CONVENTION); }}; Defensive patterns
Strategy: validation
Validate before calling
static void validateOptions(Class<?> lib) throws Exception {
try {
Field f = lib.getField("OPTIONS");
if (!java.lang.reflect.Modifier.isStatic(f.getModifiers())) throw new IllegalStateException("OPTIONS not static");
if (!Map.class.isAssignableFrom(f.getType())) throw new IllegalStateException("OPTIONS not a Map");
if (f.get(null) == null) throw new IllegalStateException("OPTIONS is null");
} catch (NoSuchFieldException ok) { /* fine: no options */ }
} Type guard
boolean hasValidOptions(Class<?> c) {
try { Field f = c.getField("OPTIONS"); return Map.class.isAssignableFrom(f.getType()) && f.get(null) != null; }
catch (Exception e) { return false; }
} Try / catch
try { Native.load("mylib", MyLib.class); } catch (IllegalArgumentException e) { if (String.valueOf(e.getMessage()).contains("OPTIONS must be a public field")) { throw new IllegalStateException("Fix MyLib.OPTIONS declaration", e); } throw e; } Prevention
- Always declare OPTIONS as public static final Map<String,Object>
- Never assign null to OPTIONS; omit the field for no options
- Keep static initializers of OPTIONS exception-free
When it happens
Trigger: Calling Native.loadLibrary/Native.load on an interface that declares a static OPTIONS field whose type is not java.util.Map, is not public, is an instance (non-static) field, is initialized to a non-Map value, or whose initializer throws.
Common situations: Typo'd generics or wrong type like public static String OPTIONS; forgetting 'static'; making OPTIONS private/protected; an OPTIONS initializer that throws ExceptionInInitializerError; copy-pasting a CONSTANTS-style field pattern incorrectly.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- fieldName must be a public field of type…
- Class type <paramTypes[i].getName()> not mapped to…
- Component must be heavyweight: " + w
- Library must be a proxy class
- advise: Interface must define a value for either iid via…
AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12).
Data as JSON: /api/errors/210a36f4adcf2683.
Report an issue: GitHub.
Appendix: source
Thrown at src/com/sun/jna/Native.java:852
}
libraryOptions = typeOptions.get(mappingClass);
if (libraryOptions != null) {
typeOptions.put(type, libraryOptions); // cache for next time
return libraryOptions;
}
try {
Field field = mappingClass.getField("OPTIONS");
field.setAccessible(true);
libraryOptions = (Map<String, Object>) field.get(null);
if (libraryOptions == null) {
throw new IllegalStateException("Null options field");
}
} catch (NoSuchFieldException e) {
libraryOptions = Collections.<String, Object>emptyMap();
} catch (Exception e) {
throw new IllegalArgumentException("OPTIONS must be a public field of type java.util.Map (" + e + "): " + mappingClass);
}
// Make a clone of the original options
libraryOptions = new HashMap<>(libraryOptions);
if (!libraryOptions.containsKey(Library.OPTION_TYPE_MAPPER)) {
libraryOptions.put(Library.OPTION_TYPE_MAPPER, lookupField(mappingClass, "TYPE_MAPPER", TypeMapper.class));
}
if (!libraryOptions.containsKey(Library.OPTION_STRUCTURE_ALIGNMENT)) {
libraryOptions.put(Library.OPTION_STRUCTURE_ALIGNMENT, lookupField(mappingClass, "STRUCTURE_ALIGNMENT", Integer.class));
}
if (!libraryOptions.containsKey(Library.OPTION_STRING_ENCODING)) {
libraryOptions.put(Library.OPTION_STRING_ENCODING, lookupField(mappingClass, "STRING_ENCODING", String.class));
}
libraryOptions = cacheOptions(mappingClass, libraryOptions, null);
// Store the original lookup class, if different from the mapping class
if (type != mappingClass) {
typeOptions.put(type, libraryOptions);
}
return libraryOptions;View on GitHub (pinned to d036ad9781)