NationalSecurityAgency/ghidra · error · IllegalArgumentException

operandIndex

Error message

operandIndex

What it means

Thrown by DBTraceReferenceSpace.addMemoryReference when operandIndex < -1. In Ghidra's reference model the operand index identifies which instruction operand a reference attaches to: 0..N is an operand index and -1 means 'instruction-level / no specific operand'. Any value below -1 is therefore meaningless and rejected.

Source

Thrown at Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/symbol/DBTraceReferenceSpace.java:475

		// NOTE: Always call with the write lock
		for (DBTraceReferenceEntry ent : List.copyOf(referenceMapSpace
				.reduce(TraceAddressSnapRangeQuery
						.intersecting(new AddressRangeImpl(fromAddress, fromAddress), span))
				.values())) {
			if (!ent.toRange.equals(toRange) || ent.opIndex != operandIndex) {
				continue;
			}

			// This sends events and updates primary. Do I want that here?
			DBTraceUtils.makeWay(ent, span, (e, s) -> e.setLifespan(s), e -> e.ref.delete());
		}
	}

	@Override
	public DBTraceReference addMemoryReference(Lifespan lifespan, Address fromAddress,
			AddressRange toRange, RefType refType, SourceType source, int operandIndex) {
		if (operandIndex < -1) {
			throw new IllegalArgumentException("operandIndex");
		}
		try (LockHold hold = LockHold.lock(lock.writeLock())) {
			makeWay(lifespan, fromAddress, toRange, operandIndex);

			DBTraceReferenceEntry entry = referenceMapSpace.put(fromAddress, lifespan, null);
			entry.set(toRange, -1, refType, operandIndex, 0, false, TypeEnum.MEMORY, source);
			DBTraceReference ref = TypeEnum.MEMORY.construct(entry);
			entry.ref = ref;
			manager.doAddXRef(entry);
			return ref;
		}
	}

	private boolean isExternalBlockAddress(Lifespan lifespan, Address addr) {
		// TODO: Verify that this works for emulation
		TraceMemoryRegion region =
			trace.getMemoryManager().getRegionContaining(lifespan.lmin(), addr);
		return region != null &&

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Pass -1 for instruction-level references, or a non-negative operand index.
  2. Clamp the computed index: int idx = Math.max(-1, computed);
  3. Trace where a negative-below-minus-one value originates and fix the upstream calculation.

Example fix

// before
refMgr.addMemoryReference(span, from, toRange, RefType.DATA, SourceType.USER, computedIndex /* -2 */);

// after
int opIdx = computedIndex < 0 ? -1 : computedIndex;
refMgr.addMemoryReference(span, from, toRange, RefType.DATA, SourceType.USER, opIdx);
Defensive patterns

Strategy: validation

Validate before calling

int opIdx = computedIndex < -1 ? -1 : computedIndex; // -1 = instruction-level
refMgr.addMemoryReference(span, from, toRange, refType, source, opIdx);

Type guard

static boolean validOperandIndex(int opIdx) { return opIdx >= -1; }

Prevention

When it happens

Trigger: Calling addMemoryReference(lifespan, fromAddress, toRange, refType, source, operandIndex) with operandIndex <= -2, often from a default-initialized int field, an uninitialised array element, or a computed index that underflows.

Common situations: Importers/parsers that compute operandIndex from a negative base; passing 0 then decrementing; misreading the API and sending a sentinel like -2; porting code that used a different sentinel convention.

Related errors


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