NationalSecurityAgency/ghidra · error · IllegalArgumentException

No variant codec for class {}

Error message

No variant codec for class {}

What it means

Thrown (unchecked IllegalArgumentException) by PrimitiveCodec.getCodec(Class) when the given class has no registered variant codec. Variant fields (Object-typed) support a fixed set of primitive/Array/String/Address/RecRange types registered in CODECS_BY_CLASS (BOOL, BYTE, CHAR, SHORT, INT, LONG, STRING, the matching arrays, ADDRESS, RANGE — notably no float/double). Any other class is unsupported.

Source

Thrown at Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreFactory.java:1016

					SHORT_ARR, INT_ARR, LONG_ARR, STRING_ARR, ADDRESS, RANGE)
				.collect(Collectors.toMap(c -> c.getSelector(), c -> c));
		Map<Class<?>, PrimitiveCodec<?>> CODECS_BY_CLASS = CODECS_BY_SELECTOR.values()
				.stream()
				.collect(Collectors.toMap(c -> c.getValueClass(), c -> c));

		/**
		 * Get the codec for the given type
		 * 
		 * @param <T> the type
		 * @param cls the class describing {@link T}
		 * @return the codec
		 * @throws IllegalArgumentException if the type is not supported
		 */
		static <T> PrimitiveCodec<T> getCodec(Class<T> cls) {
			@SuppressWarnings("unchecked")
			PrimitiveCodec<T> obj = (PrimitiveCodec<T>) CODECS_BY_CLASS.get(cls);
			if (obj == null) {
				throw new IllegalArgumentException("No variant codec for class " + cls);
			}
			return obj;
		}

		/**
		 * Get the codec for the given selector
		 * 
		 * @param sel the selector
		 * @return the codec
		 * @throws IllegalArgumentException if the selector is unknown
		 */
		static PrimitiveCodec<?> getCodec(byte sel) {
			PrimitiveCodec<?> obj = CODECS_BY_SELECTOR.get(sel);
			if (obj == null) {
				throw new IllegalArgumentException("No variant codec with selector " + sel);
			}
			return obj;
		}

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Store only a supported variant type (boolean/byte/char/short/int/long, String, the matching arrays, Address, or RecRange). Convert other types before storage.
  2. For float/double (deliberately unsupported), encode as a long via Float.floatToIntBits / Double.doubleToLongBits and store the long, or use a dedicated typed field.
  3. For custom objects, serialize to a String or byte[] and store that, or define a proper annotated sub-store.

Example fix

// before
variantField.set(myObject.getScore()); // Float -> "No variant codec for class java.lang.Float"

// after: encode unsupported numeric types to a supported one
variantField.set(Float.floatToIntBits(myObject.getScore())); // stored as Integer
Defensive patterns

Strategy: validation

Validate before calling

// Only store supported variant types
Set<Class<?>> supported = Set.of(boolean.class, byte.class, char.class, short.class,
    int.class, long.class, String.class, Address.class, RecRange.class /* + matching arrays */);
if (!supported.contains(value.getClass())) {
    // convert (e.g. float -> int bits, custom -> String/byte[]) before storing
}

Prevention

When it happens

Trigger: Storing a value whose class isn't in the supported variant set into a variant (Object-typed) DB field — e.g. a Float/Double (explicitly unsupported, per the 'No floats?' comment), a custom object, or a BigInteger. getCodec is called to pick the encoder and finds none.

Common situations: Putting a float/double into a variant field; storing a custom/domain class instead of a primitive; storing a Long where the registry only has the primitive-backed wrapper; assuming arbitrary Object types are supported.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/dd4bb83555d8d225. Report an issue: GitHub.