NationalSecurityAgency/ghidra · critical · RuntimeException

Saveable must have a default constructor

Error message

Saveable must have a default constructor

What it means

Thrown by AbstractDBTracePropertyMap.doLoad() when the Saveable value class has no public no-arg (default) constructor, caught as NoSuchMethodException from obj.valueClass.getConstructor(). The deserialization framework requires a default constructor to create a fresh instance before calling restore(). This is a RuntimeException indicating a contract violation: every Saveable stored in a property map must have a public zero-argument constructor.

Source

Thrown at Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/AbstractDBTracePropertyMap.java:449

			try {
				Saveable value = getValue(obj);
				if (value == null) {
					value = obj.valueClass.getConstructor().newInstance();
					setValue(obj, value);
				}
				ObjectStorage objStorage = new ObjectStorageStreamAdapter(
					new ObjectInputStream(new ByteArrayInputStream(enc)));
				value.restore(objStorage);
			}
			catch (IOException e) {
				throw new AssertionError(e);
			}
			catch (InstantiationException | InvocationTargetException | SecurityException e) {
				throw new RuntimeException(
					"Could not instantiate saveable of type " + obj.valueClass);
			}
			catch (NoSuchMethodException e) {
				throw new RuntimeException("Saveable must have a default constructor");
			}
		}
	}

	public static class DBTraceStringPropertyMap
			extends AbstractDBTracePropertyMap<String, DBTraceStringPropertyMapEntry> {

		public DBTraceStringPropertyMap(String name, DBHandle dbh, OpenMode openMode,
				ReadWriteLock lock, TaskMonitor monitor, Language baseLanguage, DBTrace trace,
				DBTraceThreadManager threadManager) throws IOException, VersionException {
			super(name, dbh, openMode, lock, monitor, baseLanguage, trace, threadManager,
				DBTraceStringPropertyMapEntry.class, DBTraceStringPropertyMapEntry::new);
		}

		@Override
		public Class<String> getValueClass() {
			return String.class;
		}

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Add a public no-arg constructor to the Saveable value class: public MySaveable() {}.
  2. Ensure the constructor is public (not private/protected/package-private) since getConstructor() only finds public constructors.
  3. If the class legitimately requires parameters, use a factory-based approach or store the configuration in the serialized data and read it in restore().

Example fix

// before
public class MySaveable implements Saveable {
    public MySaveable(int requiredParam) { ... }
}

// after
public class MySaveable implements Saveable {
    private int param = 0;
    public MySaveable() {} // required by property map framework
    public MySaveable(int requiredParam) { this.param = requiredParam; }
}
Defensive patterns

Strategy: validation

Validate before calling

// Verify a default constructor exists before registering a Saveable
public static void assertDefaultConstructor(Class<? extends Saveable> cls) {
    try {
        java.lang.reflect.Constructor<?> c = cls.getConstructor();
        if (!java.lang.reflect.Modifier.isPublic(c.getModifiers())) {
            throw new IllegalStateException(
                cls + " needs a PUBLIC no-arg constructor");
        }
    } catch (NoSuchMethodException e) {
        throw new IllegalStateException(
            cls + " must have a public no-arg constructor for property maps", e);
    }
}

Prevention

When it happens

Trigger: Loading a trace database where a property map's value class (registered via the map's valueClass parameter) lacks a public no-arg constructor. The doLoad method calls getConstructor() (no arguments) which throws NoSuchMethodException when no matching constructor exists.

Common situations: A Saveable class was refactored to add required constructor parameters without keeping a default constructor. A custom Saveable was registered in a property map but only has parameterized constructors. Version mismatch where the class changed its constructor set between writing and reading the trace.

Related errors


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