NationalSecurityAgency/ghidra · error · TraceRmiError

snap or time required

Error message

snap or time required

What it means

Thrown by TraceRmiHandler.handleSnapshot when the RequestSnapshot's time case is TIME_NOT_SET — neither a snap number nor a schedule string was supplied. Creating a snapshot requires a time anchor, so the request is rejected with TraceRmiError.

Source

Thrown at Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiHandler.java:1187

		OpenTrace open = requireOpenTrace(req.getOid());
		Object objVal = open.toValue(value.getValue());
		TraceObject object = open.getObject(value.getParent(), objVal != null);
		if (object == null) {
			// Implies request was to set value to null
			return ReplySetValue.newBuilder().setSpan(makeSpan(Lifespan.EMPTY)).build();
		}

		TraceObjectValue val = object.setValue(toLifespan(value.getSpan()), value.getKey(), objVal,
			toResolution(req.getResolution()));
		return ReplySetValue.newBuilder()
				.setSpan(makeSpan(val == null ? Lifespan.EMPTY : val.getLifespan()))
				.build();
	}

	protected ReplySnapshot handleSnapshot(RequestSnapshot req) {
		OpenTrace open = requireOpenTrace(req.getOid());
		TraceSnapshot snapshot = switch (req.getTimeCase()) {
			case TIME_NOT_SET -> throw new TraceRmiError("snap or time required");
			case SNAP -> open.createSnapshot(req.getSnap().getSnap());
			case SCHEDULE -> open.createSnapshot(
				TraceSchedule.parse(req.getSchedule().getSchedule(), TimeRadix.DEC));
		};
		snapshot.setDescription(req.getDescription());
		if (!"".equals(req.getDatetime())) {
			Instant instant =
				DateTimeFormatter.ISO_INSTANT.parse(req.getDatetime()).query(Instant::from);
			snapshot.setRealTime(instant.toEpochMilli());
		}
		return ReplySnapshot.newBuilder()
				.setSnap(Snap.newBuilder().setSnap(snapshot.getKey()))
				.build();
	}

	protected ReplyStartTx handleStartTx(RequestStartTx req) {
		OpenTrace open = requireOpenTrace(req.getOid());
		Tid tid = requireAvailableTid(open, req.getTxid());

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Always set exactly one of setSnap (numeric snapshot) or setSchedule (a TraceSchedule string) on RequestSnapshot.
  2. If you want 'current', explicitly pass the current snap number rather than leaving it unset.
  3. Validate the request's getTimeCase() != TIME_NOT_SET before sending.

Example fix

// before
RequestSnapshot req = RequestSnapshot.newBuilder().setOid(oid).build();

// after
RequestSnapshot req = RequestSnapshot.newBuilder()
    .setOid(oid)
    .setSnap(Snap.newBuilder().setSnap(currentSnap))
    .build();
Defensive patterns

Strategy: validation

Validate before calling

if (req.getTimeCase() == RequestSnapshot.TimeCase.TIME_NOT_SET) {
    throw new IllegalArgumentException("snapshot requires snap or schedule");
}

Type guard

boolean hasTime(RequestSnapshot req) {
    return req.getTimeCase() != RequestSnapshot.TimeCase.TIME_NOT_SET;
}

Try / catch

try {
    handler.snapshot(req);
} catch (TraceRmiError e) {
    // no time set; set snap or schedule and retry
}

Prevention

When it happens

Trigger: The client builds a RequestSnapshot without calling setSnap or setSchedule, leaving the time oneof unset.

Common situations: Client omits the time field assuming a default; code path that constructs the request conditionally skips both branches; schema confusion about which field is required.

Related errors


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