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
- Add a public no-arg constructor to the Saveable value class: public MySaveable() {}.
- Ensure the constructor is public (not private/protected/package-private) since getConstructor() only finds public constructors.
- 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
- Every Saveable in a property map must have a public no-arg constructor.
- Add a unit test that reflectively checks for the default constructor.
- Never remove the default constructor from a Saveable that is persisted in property maps.
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
- Could not instantiate saveable of type
- Can only directly delete values for maps where the entry is
- Can only directly delete values for maps where the entry is
- The given entry is not in this space
- Platform and prototype disagree in language
AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14).
Data as JSON: /api/errors/9f45ecf8b2f8311a.
Report an issue: GitHub.