{"record":{"id":"73a5a3147f7a7fdd","repo":"NationalSecurityAgency/ghidra","slug":"set-end-snap-of-root-unit","errorCode":null,"errorMessage":"Set end-snap of root unit","messagePattern":"Set end-snap of root unit","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java","lineNumber":127,"sourceCode":"\n\t@Override\n\tpublic Language getLanguage() {\n\t\treturn root.getLanguage();\n\t}\n\n\t@Override\n\tpublic Lifespan getLifespan() {\n\t\treturn root.getLifespan();\n\t}\n\n\t@Override\n\tpublic long getStartSnap() {\n\t\treturn root.getStartSnap();\n\t}\n\n\t@Override\n\tpublic void setEndSnap(long endSnap) {\n\t\tthrow new UnsupportedOperationException(\"Set end-snap of root unit\");\n\t}\n\n\t@Override\n\tpublic long getEndSnap() {\n\t\treturn root.getEndSnap();\n\t}\n\n\t@Override\n\tpublic Address getAddress() {\n\t\treturn address;\n\t}\n\n\t@Override\n\tpublic Address getMaxAddress() {\n\t\treturn maxAddress;\n\t}\n\n\t@Override","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java#L109-L145","documentation":"Thrown by AbstractDBTraceDataComponent.setEndSnap() because a data component inherits its lifespan from its root data unit — you cannot set the end snap on an individual component. The component's lifespan is a projection of the root unit's lifespan, so changing it independently is meaningless. To modify the lifespan, operate on the root unit's setEndSnap() instead.","triggerScenarios":"Calling component.setEndSnap(n) on a data component object — any attempt to modify the temporal extent of a single sub-field within a composite data unit.","commonSituations":"Scripting that loops over data components and tries to adjust their lifespans; confusing component-level APIs with root-level APIs; generic code that treats all data values uniformly and calls setEndSnap.","solutions":["Operate on the root unit: component.getRoot().setEndSnap(endSnap).","If you need component-level temporal control, restructure so each sub-value is its own independent data unit rather than a component of a composite."],"exampleFix":"// before\nfor (var component : root.getDataComponents()) {\n    component.setEndSnap(50); // throws\n}\n\n// after — set on the root\nroot.setEndSnap(50);","handlingStrategy":"validation","validationCode":"// Never call setEndSnap on a component; route to the root\nif (dataValue instanceof TraceDataComponent) {\n    ((TraceDataComponent) dataValue).getRoot().setEndSnap(endSnap);\n} else if (dataValue instanceof TraceData) {\n    ((TraceData) dataValue).setEndSnap(endSnap);\n}","typeGuard":"static boolean isModifiableLifespan(Object dataValue) {\n    return dataValue instanceof TraceData && !(dataValue instanceof TraceDataComponent);\n}","tryCatchPattern":"try {\n    dataValue.setEndSnap(endSnap);\n} catch (UnsupportedOperationException e) {\n    if (e.getMessage().contains(\"root unit\")) {\n        // Route to root\n        ((TraceData) dataValue).getRoot().setEndSnap(endSnap);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always call setEndSnap on the root data unit, never on a component.","Type-check for TraceDataComponent before calling lifespan-modifying methods.","Design generic handlers to detect component vs. root and delegate appropriately."],"tags":["data-component","lifespan","setendsnap","trace-modeling"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}