NationalSecurityAgency/ghidra · error · IllegalArgumentException

all function symbols are not from the same program

Error message

all function symbols are not from the same program

What it means

Thrown by the SFQueryInfo constructor when the function set contains symbols from more than one Program. The constructor locks in the first symbol's program and rejects any symbol whose getProgram() differs, because a single QueryNearest must run against one signature/address context. Third ordered check.

Source

Thrown at Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/facade/SFQueryInfo.java:69

	 * @throws IllegalArgumentException if {@code functions} is {@code null}/empty or functions
	 * are from multiple programs.  
	 */
	public SFQueryInfo(Set<FunctionSymbol> functions) {
		if (functions == null) {
			throw new IllegalArgumentException("Function list cannot be null");
		}

		if (functions.isEmpty()) {
			throw new IllegalArgumentException("Function list cannot be empty");
		}

		this.functions = functions;
		for (FunctionSymbol s : functions) {
			if (program == null) {
				program = s.getProgram();
			}
			else if (program != s.getProgram()) {
				throw new IllegalArgumentException(
					"all function symbols are not from the same program");
			}
		}
		queryNearest = new QueryNearest();
		bsimFilter = new BSimFilter();
		preFilter = new PreFilter();
	}

	/**
	 * @return the program from which all queried functions are from
	 */
	public Program getProgram() {
		return program;
	}

	/**
	 * Gets the threshold under which a potential similar function will not be matched.  This
	 * threshold is for how similar the potential function is. This is a value from 0.0 to 1.0. The 

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Partition the set by getProgram() and issue one SFQueryInfo per program.
  2. Scope selection gathering to the current/active program.
  3. Drop external/library symbols (different Program) from the set before construction.

Example fix

// before
Set<FunctionSymbol> mixed = gatherAcrossPrograms();
SFQueryInfo info = new SFQueryInfo(mixed);
// after
Map<Program, List<Set<FunctionSymbol>>> grouped = mixed.stream()
    .collect(Collectors.groupingBy(FunctionSymbol::getProgram));
grouped.values().forEach(syms -> {
    SFQueryInfo info = new SFQueryInfo(new HashSet<>(syms));
});
Defensive patterns

Strategy: validation

Validate before calling

Map<Program, Set<FunctionSymbol>> byProgram = functions.stream()
    .collect(Collectors.groupingBy(FunctionSymbol::getProgram, Collectors.toSet()));
for (Set<FunctionSymbol> per : byProgram.values()) {
    SFQueryInfo info = new SFQueryInfo(per);
}

Type guard

static boolean singleProgram(Set<FunctionSymbol> fns) {
    return fns.stream().map(FunctionSymbol::getProgram).distinct().count() <= 1;
}

Try / catch

try {
    new SFQueryInfo(functions);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("same program")) { /* split per program */ }
}

Prevention

When it happens

Trigger: Passing a set assembled across multiple open programs, or mixing main-program symbols with external/library symbols that report a different Program.

Common situations: Cross-program selection; a script that aggregates symbols from several Program objects into one set; selection spanning a program and its imported library.

Related errors


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