NationalSecurityAgency/ghidra · error · IllegalArgumentException

Unsupported value class: {}

Error message

Unsupported value class: {}

What it means

Thrown by DBTraceAddressPropertyManager.doCreateMap when the requested property value class is none of the supported types. Supported classes are exactly Integer, Long, String, Void, or any subclass of ghidra.framework.save.Saveable. Any other Class<T> (e.g. Double, Boolean, Address, a plain POJO) reaches the terminal throw. This is a programming contract violation rather than a runtime data problem.

Source

Thrown at Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/property/DBTraceAddressPropertyManager.java:167

			}
			if (valueClass == Void.class) {
				return (AbstractDBTracePropertyMap<T, ?>) new DBTraceVoidPropertyMap(tableName, dbh,
					openMode, lock, TaskMonitor.DUMMY, baseLanguage, trace, threadManager);
			}
			if (Saveable.class.isAssignableFrom(valueClass)) {
				Class<? extends Saveable> saveableClass = valueClass.asSubclass(Saveable.class);
				return (AbstractDBTracePropertyMap<T, ?>) new DBTraceSaveablePropertyMap<>(
					tableName, dbh, openMode, lock, TaskMonitor.DUMMY, baseLanguage, trace,
					threadManager, saveableClass);
			}
		}
		catch (IOException e) {
			dbError(e);
		}
		catch (VersionException e) {
			throw new AssertionError(e);
		}
		throw new IllegalArgumentException("Unsupported value class: " + valueClass);
	}

	@Override
	public <T> AbstractDBTracePropertyMap<T, ?> createPropertyMap(String name, Class<T> valueClass)
			throws DuplicateNameException {
		try (LockHold hold = LockHold.lock(lock.writeLock())) {
			if (propertyMapsByName.containsKey(name)) {
				throw new DuplicateNameException(name);
			}
			DBTraceAddressPropertyEntry ent = propertyStore.create();
			ent.set(name, valueClass);
			AbstractDBTracePropertyMap<T, ?> map = doCreateMap(name, OpenMode.CREATE, valueClass);
			ent.map = map;
			propertyMapsByName.put(name, map);
			return map;
		}
	}

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Use one of the built-in classes: Integer, Long, String, or Void.
  2. For complex types, implement a class extending ghidra.framework.save.Saveable and pass its Class.
  3. If you need a boolean, store it as a Void presence marker or wrap in a Saveable.

Example fix

// before
propMgr.createPropertyMap("flag", Boolean.class);
// after
propMgr.createPropertyMap("flag", Integer.class); // store 0/1
// or implement BooleanSaveable extends Saveable
Defensive patterns

Strategy: type-guard

Validate before calling

boolean supported = valueClass == Integer.class || valueClass == Long.class ||
    valueClass == String.class || valueClass == Void.class ||
    Saveable.class.isAssignableFrom(valueClass);
if (!supported) throw new IllegalArgumentException("unsupported");
mgr.createPropertyMap(name, valueClass);

Type guard

static boolean isSupportedValueClass(Class<?> c) {
    return c == Integer.class || c == Long.class || c == String.class
        || c == Void.class || Saveable.class.isAssignableFrom(c);
}

Prevention

When it happens

Trigger: Calling createPropertyMap(name, Double.class), createPropertyMap(name, Address.class), or getOrCreatePropertyMap with an arbitrary custom class that is not a Saveable subclass.

Common situations: Developer assumes arbitrary value types are allowed and passes Boolean/Double/byte[]. Passing a Saveable subclass that fails the isAssignableFrom check due to a raw/generic Class token erasure.

Related errors


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