{"record":{"id":"e3b879e030d29dcc","repo":"java-native-access/jna","slug":"options-must-be-a-public-field-of-type-java-util-map-cause","errorCode":null,"errorMessage":"OPTIONS must be a public field of type java.util.Map (<cause>): <mappingClass>","messagePattern":"OPTIONS must be a public field of type java\\.util\\.Map \\(<cause>\\): <mappingClass>","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/com/sun/jna/Native.java","lineNumber":853,"sourceCode":"\n        libraryOptions = typeOptions.get(mappingClass);\n        if (libraryOptions != null) {\n            typeOptions.put(type, libraryOptions);  // cache for next time\n            return libraryOptions;\n        }\n\n        try {\n            Field field = mappingClass.getField(\"OPTIONS\");\n            field.setAccessible(true);\n            libraryOptions = (Map<String, Object>) field.get(null);\n            if (libraryOptions == null) {\n                throw new IllegalStateException(\"Null options field\");\n            }\n        } catch (NoSuchFieldException e) {\n            libraryOptions = Collections.<String, Object>emptyMap();\n        } catch (Exception e) {\n            throw new IllegalArgumentException(\"OPTIONS must be a public field of type java.util.Map (\" + e + \"): \" + mappingClass);\n        }\n        // Make a clone of the original options\n        libraryOptions = new HashMap<>(libraryOptions);\n        if (!libraryOptions.containsKey(Library.OPTION_TYPE_MAPPER)) {\n            libraryOptions.put(Library.OPTION_TYPE_MAPPER, lookupField(mappingClass, \"TYPE_MAPPER\", TypeMapper.class));\n        }\n        if (!libraryOptions.containsKey(Library.OPTION_STRUCTURE_ALIGNMENT)) {\n            libraryOptions.put(Library.OPTION_STRUCTURE_ALIGNMENT, lookupField(mappingClass, \"STRUCTURE_ALIGNMENT\", Integer.class));\n        }\n        if (!libraryOptions.containsKey(Library.OPTION_STRING_ENCODING)) {\n            libraryOptions.put(Library.OPTION_STRING_ENCODING, lookupField(mappingClass, \"STRING_ENCODING\", String.class));\n        }\n        libraryOptions = cacheOptions(mappingClass, libraryOptions, null);\n        // Store the original lookup class, if different from the mapping class\n        if (type != mappingClass) {\n            typeOptions.put(type, libraryOptions);\n        }\n        return libraryOptions;\n    }","sourceCodeStart":835,"sourceCodeEnd":871,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/src/com/sun/jna/Native.java#L835-L871","documentation":"JNA reads the public static OPTIONS field of a library mapping class and expects it to be a Map<String, Object>. If the field exists but cannot be retrieved as such (ClassCastException on the cast, IllegalAccessException, or other reflection failure other than NoSuchFieldException), JNA throws IllegalArgumentException('OPTIONS must be a public field of type java.util.Map (...)') naming the mapping class.","triggerScenarios":"Declaring 'public static OPTIONS' with a non-Map type (e.g. Properties, HashMap<String,String> assigned where cast fails is fine, but String/int/Map-typed raw mismatch), or a non-public/otherwise inaccessible OPTIONS field that throws IllegalAccessException on get().","commonSituations":"Typing OPTIONS as something other than java.util.Map; making OPTIONS private/protected and expecting JNA to read it; using a custom Map subtype incompatible with the (Map<String, Object>) cast under strict generics; IDE-generated field with wrong type.","solutions":["Declare OPTIONS exactly as: public static final Map<String, Object> OPTIONS = new HashMap<>();","Make the field public so field.get(null) does not throw IllegalAccessException.","Convert non-Map option holders (Properties, JSON config) into a Map<String, Object> before assignment.","If no options are needed, delete the field — JNA defaults to an empty map on NoSuchFieldException."],"exampleFix":"// before\npublic interface MyLib extends Library {\n    public static final Properties OPTIONS = new Properties(); // not a Map -> IllegalArgumentException\n}\n// after\npublic interface MyLib extends Library {\n    Map<String, Object> OPTIONS = new HashMap<>();\n}","handlingStrategy":"validation","validationCode":"Field f = mappingClass.getField(\"OPTIONS\");\nif (!Map.class.isAssignableFrom(f.getType())) {\n    throw new IllegalArgumentException(\"OPTIONS must be java.util.Map, found \" + f.getType());\n}\nif (!Modifier.isPublic(f.getModifiers())) {\n    throw new IllegalArgumentException(\"OPTIONS must be public\");\n}","typeGuard":"boolean validOptionsField(Class<?> c) {\n    try {\n        Field f = c.getField(\"OPTIONS\");\n        return Map.class.isAssignableFrom(f.getType()) && Modifier.isPublic(f.getModifiers());\n    } catch (NoSuchFieldException e) {\n        return true; // optional field\n    }\n}","tryCatchPattern":"try {\n    lib = Native.load(name, mappingClass, options);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"OPTIONS must be a public field\")) {\n        throw new IllegalStateException(\"Fix \" + mappingClass.getSimpleName()\n            + \".OPTIONS: declare 'public static final Map<String, Object> OPTIONS'\", e);\n    }\n    throw e;\n}","preventionTips":["Always declare OPTIONS as public static final Map<String, Object>.","Keep option keys as constants from Library.OPTION_* to avoid other option-related failures.","Add a reflection-based startup test validating the mapping class shape before native load."],"tags":["jna","options","type-mismatch","reflection"],"backgroundTag":"config-type-mismatch","analyzedSha":"d036ad9781adad4b66693e8fa7098e4ac665e0a3","analyzedAt":"2026-09-12T06:50:59.239Z","contentChangedAt":"2026-09-12T06:50:59.239Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}