java-native-access/jna · error · IllegalArgumentException

Unsupported type " + cls

Error message

Unsupported type " + cls

What it means

Structure.getTypeInfo(Object, Class) maps Java field values to libffi FFIType descriptors for a known set of types (primitives, boxed types, Pointer, String, WString, arrays, Structure, Callback, etc.). If the class is not one of the supported native-mappable types, it throws IllegalArgumentException with "Unsupported type <cls>".

Source

Thrown at src/com/sun/jna/Structure.java:2266

                }
            }
            finally {
                cacheStructureLock.writeLock().unlock();
            }

            if (NativeMapped.class.isAssignableFrom(cls)) {
                NativeMappedConverter c = NativeMappedConverter.getInstance(cls);
                return get(c.toNative(obj, new ToNativeContext()), c.nativeType());
            }

            if (cls.isArray()) {
                FFIType type = new FFIType(obj, cls);
                // Store it in the map to prevent premature GC of type info
                storeTypeInfo(cls, Array.getLength(obj), type);
                return type;
            }

            throw new IllegalArgumentException("Unsupported type " + cls);
        }

        private static FFIType getTypeInfo(Class clazz, int elementCount) {
            cacheStructureLock.readLock().lock();
            try {
                Map<Integer, FFIType> typeMap = typeInfoMap.get(clazz);
                if (typeMap != null) {
                    return typeMap.get(elementCount);
                } else {
                    return null;
                }
            }
            finally {
                cacheStructureLock.readLock().unlock();
            }
        }

        private static void storeTypeInfo(Class clazz, FFIType type) {

View on GitHub (pinned to d036ad9781)

Solutions

  1. Change the Structure field to a natively mappable type (int, long, double, boolean, byte/short/char, Pointer, String, WString, nested Structure, arrays, or ByReference variants)
  2. If the field has a natural native representation, provide a custom TypeMapper (or @Structure.FieldOrder-safe mapper/ToNativeConverter) registered via Native.setTypeMapper or the Structure constructor
  3. For complex data, flatten into supported primitives or pass as a nested Structure/Pointer with explicit marshalling
  4. Check which field triggers it: the message names the exact class; compare against Structure.FFIType-supported types in the JNA docs

Example fix

// before
public class S extends Structure {
    public BigDecimal amount; // Unsupported type java.math.BigDecimal
}
// after
public class S extends Structure {
    public long amountCents; // map in application code
}
Defensive patterns

Strategy: type-guard

Validate before calling

// JNA-supported native-mappable field types (Java types usable directly in Structures)
Set<Class<?>> NATIVE_TYPES = new HashSet<>(Arrays.asList(
    boolean.class, byte.class, short.class, char.class, int.class,
    long.class, float.class, double.class,
    Boolean.class, Byte.class, Short.class, Character.class, Integer.class,
    Long.class, Float.class, Double.class,
    Pointer.class, String.class, WString.class));
void checkFields(Class<?> s) {
    for (Field f : s.getFields()) {
        Class<?> t = f.getType();
        if (!NATIVE_TYPES.contains(t) && !Structure.class.isAssignableFrom(t)
            && !Callback.class.isAssignableFrom(t) && !t.isArray()
            && !FromNativeConverter.class.isAssignableFrom(t)) {
            throw new IllegalArgumentException("Field not natively mappable: " + f);
        }
    }
}

Type guard

boolean isNativeMappable(Class<?> t) {
    return t.isPrimitive()
        || Number.class.isAssignableFrom(t) || t == Boolean.class || t == Character.class
        || t == Pointer.class || t == String.class || t == WString.class
        || Structure.class.isAssignableFrom(t) || Callback.class.isAssignableFrom(t)
        || t.isArray();
}

Try / catch

try {
    structure.read();
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Unsupported type ")) {
        throw new IllegalStateException("Replace/natively map field of type: "
            + e.getMessage().substring("Unsupported type ".length()), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Declaring a Structure field of a type JNA cannot map to native memory (e.g. java.math.BigDecimal, java.util.Date, an arbitrary POJO, or an unsupported generic type) and then reading/writing the structure or calling Native.getNativeSize on it.

Common situations: Mapping a struct that contains a field type not supported by JNA (auto-boxed custom wrappers, Optional, collections), refactoring a field from a supported type to an unsupported one, or forgetting a TypeMapper/Structure.FieldOffset mapping for the field.

Related errors


AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12). Data as JSON: /api/errors/5eb97d561889d2d0. Report an issue: GitHub.