NationalSecurityAgency/ghidra · error · CodeUnitInsertionException

Code units cannot overlap

Error message

Code units cannot overlap

What it means

Thrown by truncateSoonestDefined in AbstractBaseDBTraceDefinedUnitsView when computing the truncation bound for a code unit's lifespan and an existing code unit's start snap is at or before the requested span's minimum. This means a defined code unit already occupies the time range being requested, so the new/modified unit cannot be placed without overlapping it. The truncation logic is meant to shrink the lifespan to just before the next unit, but if the next unit starts at or before the span's beginning, there is no room to truncate into.

Source

Thrown at Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractBaseDBTraceDefinedUnitsView.java:404

		if (extending == null) {
			toScan = span;
		}
		else if (span.lmax() <= extending.getEndSnap()) {
			// we're shrinking or staying the same, so not possible to collide with others
			return span;
		}
		else {
			toScan = span.withMin(extending.getEndSnap() + 1);
		}
		T truncateBy =
			mapSpace.reduce(TraceAddressSnapRangeQuery.intersecting(range, toScan)
					.starting(Rectangle2DDirection.BOTTOMMOST))
					.firstValue();
		if (truncateBy == null) {
			return span;
		}
		if (truncateBy.getStartSnap() <= span.lmin()) {
			throw new CodeUnitInsertionException("Code units cannot overlap");
		}
		return span.withMax(truncateBy.getStartSnap() - 1);
	}

	protected long computeTruncatedMax(Lifespan lifespan, T extending, AddressRange range)
			throws CodeUnitInsertionException {
		// First, truncate lifespan to the next code unit when upper bound is max
		if (!lifespan.maxIsFinite()) {
			lifespan = space.instructions.truncateSoonestDefined(lifespan, extending, range);
			lifespan = space.definedData.truncateSoonestDefined(lifespan, extending, range);
		}
		// Second, truncate lifespan to the next change of bytes in the range
		DBTraceMemorySpace memSpace =
			space.trace.getMemoryManager().getMemorySpace(space.space, true);
		Lifespan fullSpan = extending == null ? lifespan : lifespan.bound(extending.getLifespan());
		long endSnap = memSpace.getFirstChange(fullSpan, range);
		if (endSnap == Long.MIN_VALUE) {
			return lifespan.lmax();

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Delete or shorten the existing conflicting code unit before inserting the new one: call codeUnit.getLifespan() and delete it for the overlapping snaps.
  2. Narrow the requested lifespan so its start snap is before the conflicting unit's start snap (the method will truncate to fit).
  3. Use a different address range or snapshot range that does not collide with existing defined units.

Example fix

// before
view.create(Lifespan.span(0, 100), address, platform, dataType);
// fails if a unit exists at snap 50 at the same address

// after — clear the conflicting range first
DefinedData existing = view.getContaining(50, address);
if (existing != null) {
    existing.delete(); // or existing.setLifespan(...) to shrink
}
view.create(Lifespan.span(0, 100), address, platform, dataType);
Defensive patterns

Strategy: try-catch

Validate before calling

// Before defining a unit, check for overlapping defined units
Lifespan span = Lifespan.span(startSnap, endSnap);
AddressRange range = new AddressRangeImpl(address, address.add(length - 1));
Collection<? extends CodeUnit> conflicts =
    space.definedUnits.getIntersecting(new ImmutableTraceAddressSnapRange(range, span));
if (!conflicts.isEmpty()) {
    // delete or shrink conflicting units first
    for (CodeUnit cu : conflicts) {
        // handle conflict
    }
}

Try / catch

try {
    view.create(lifespan, address, platform, dataType);
} catch (CodeUnitInsertionException e) {
    if (e.getMessage().equals("Code units cannot overlap")) {
        // Identify and clear the conflicting unit, then retry
        CodeUnit existing = view.getContaining(lifespan.lmin(), address);
        if (existing != null) {
            existing.delete();
            view.create(lifespan, address, platform, dataType);
        }
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling computeTruncatedMax or the internal truncateSoonestDefined path when an existing instruction or defined-data unit already exists at or before the earliest snap of the requested lifespan at the given address range. This occurs during create() or setEndSnap()/length override operations that invoke truncation.

Common situations: Attempting to define a code unit over a snapshot range that already contains a different code unit at the same addresses; overlapping lifespan ranges when annotating instructions/data across traces; race conditions where two operations define units at overlapping snaps without coordination.

Related errors


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