{"record":{"id":"9f45ecf8b2f8311a","repo":"NationalSecurityAgency/ghidra","slug":"saveable-must-have-a-default-constructor","errorCode":null,"errorMessage":"Saveable must have a default constructor","messagePattern":"Saveable must have a default constructor","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/AbstractDBTracePropertyMap.java","lineNumber":449,"sourceCode":"\t\t\ttry {\n\t\t\t\tSaveable value = getValue(obj);\n\t\t\t\tif (value == null) {\n\t\t\t\t\tvalue = obj.valueClass.getConstructor().newInstance();\n\t\t\t\t\tsetValue(obj, value);\n\t\t\t\t}\n\t\t\t\tObjectStorage objStorage = new ObjectStorageStreamAdapter(\n\t\t\t\t\tnew ObjectInputStream(new ByteArrayInputStream(enc)));\n\t\t\t\tvalue.restore(objStorage);\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new AssertionError(e);\n\t\t\t}\n\t\t\tcatch (InstantiationException | InvocationTargetException | SecurityException e) {\n\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\"Could not instantiate saveable of type \" + obj.valueClass);\n\t\t\t}\n\t\t\tcatch (NoSuchMethodException e) {\n\t\t\t\tthrow new RuntimeException(\"Saveable must have a default constructor\");\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class DBTraceStringPropertyMap\n\t\t\textends AbstractDBTracePropertyMap<String, DBTraceStringPropertyMapEntry> {\n\n\t\tpublic DBTraceStringPropertyMap(String name, DBHandle dbh, OpenMode openMode,\n\t\t\t\tReadWriteLock lock, TaskMonitor monitor, Language baseLanguage, DBTrace trace,\n\t\t\t\tDBTraceThreadManager threadManager) throws IOException, VersionException {\n\t\t\tsuper(name, dbh, openMode, lock, monitor, baseLanguage, trace, threadManager,\n\t\t\t\tDBTraceStringPropertyMapEntry.class, DBTraceStringPropertyMapEntry::new);\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<String> getValueClass() {\n\t\t\treturn String.class;\n\t\t}","sourceCodeStart":431,"sourceCodeEnd":467,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/map/AbstractDBTracePropertyMap.java#L431-L467","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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()."],"exampleFix":"// before\npublic class MySaveable implements Saveable {\n    public MySaveable(int requiredParam) { ... }\n}\n\n// after\npublic class MySaveable implements Saveable {\n    private int param = 0;\n    public MySaveable() {} // required by property map framework\n    public MySaveable(int requiredParam) { this.param = requiredParam; }\n}","handlingStrategy":"validation","validationCode":"// Verify a default constructor exists before registering a Saveable\npublic static void assertDefaultConstructor(Class<? extends Saveable> cls) {\n    try {\n        java.lang.reflect.Constructor<?> c = cls.getConstructor();\n        if (!java.lang.reflect.Modifier.isPublic(c.getModifiers())) {\n            throw new IllegalStateException(\n                cls + \" needs a PUBLIC no-arg constructor\");\n        }\n    } catch (NoSuchMethodException e) {\n        throw new IllegalStateException(\n            cls + \" must have a public no-arg constructor for property maps\", e);\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["property-map","trace-database","serialization","reflection","constructor","unchecked-exception"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}