NationalSecurityAgency/ghidra · error · IOException

Score file missing threshold lines

Error message

Score file missing threshold lines

What it means

Thrown by FileScoreCaching.loadCache() when the score file exists but is completely empty — the first readLine() returns null. The expected file format begins with two header lines (simThreshold then sigThreshold) followed by one line per executable (md5 + score). An empty or zero-byte file fails this invariant at the very first read.

Source

Thrown at Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/client/FileScoreCaching.java:50

		storageFile = new File(fileName);
		cacheMap = null;
		simThreshold = -1.0;		// Negative indicates thresholds have not been configured
		sigThreshold = -1.0;
	}

	private void loadCache() throws IOException {
		if (cacheMap != null) {
			return;
		}
		if (!storageFile.exists()) {
			return;							// File doesn't exist, it will get created on first commitSelfScore call
		}
		cacheMap = new TreeMap<String, Float>();
		BufferedReader reader = new BufferedReader(new FileReader(storageFile));
		String floatString = reader.readLine();
		if (floatString == null) {
			reader.close();
			throw new IOException("Score file missing threshold lines");
		}
		simThreshold = Float.parseFloat(floatString);
		floatString = reader.readLine();
		if (floatString == null) {
			reader.close();
			throw new IOException("Score file missing threshold lines");
		}
		sigThreshold = Float.parseFloat(floatString);
		floatString = reader.readLine();
		while (floatString != null) {
			String[] split = floatString.split(" ");
			if (split.length != 2 || split[0].length() != 32) {
				reader.close();
				throw new IOException("Bad line in score file");
			}
			float val = Float.parseFloat(split[1]);
			cacheMap.put(split[0], val);
			floatString = reader.readLine();

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Delete the corrupt/empty cache file so it gets regenerated with valid header lines on the next commitSelfScore call.
  2. Call FileScoreCaching.resetStorage(simThresh, sigThresh) to delete the existing file and reinitialize thresholds.
  3. Investigate what created the empty file (failed process, manual touch) to prevent recurrence.

Example fix

// before — cache file exists but is empty
FileScoreCaching cache = new FileScoreCaching(path);
cache.getSelfScore(md5); // throws IOException "Score file missing threshold lines"

// after
FileScoreCaching cache = new FileScoreCaching(path);
cache.resetStorage(simThresh, sigThresh); // deletes bad file, resets thresholds
Defensive patterns

Strategy: validation

Validate before calling

File cacheFile = new File(cachePath);
if (cacheFile.exists() && cacheFile.length() == 0) {
    cacheFile.delete(); // remove empty file so it regenerates
}

Try / catch

try {
    cache.getSelfScore(md5);
} catch (LSHException e) {
    if (e.getMessage().contains("threshold lines")) {
        cache.resetStorage(simThresh, sigThresh);
        // regenerate scores then retry
    } else throw e;
}

Prevention

When it happens

Trigger: The score cache file referenced by FileScoreCaching's constructor exists on disk but contains no data (e.g. created by a failed/touch command, truncated by a crash during write, or a race condition where the file was created but the header lines were never flushed).

Common situations: An interrupted previous run left a zero-byte cache file. A user manually created or truncated the cache file. A concurrent process or filesystem issue truncated the file between the existence check and the read.

Related errors


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