NationalSecurityAgency/ghidra · error · IllegalArgumentException

DBAnnotatedField must be non-static and non-final

Error message

DBAnnotatedField must be non-static and non-final

What it means

@DBAnnotatedField marks an instance, mutable column-value field that the factory reads and writes reflectively per row. It therefore cannot be static (no per-instance storage) nor final (cannot be overwritten from a DB record). collectFields rejects both with IllegalArgumentException.

Source

Thrown at Ghidra/Debug/ProposedUtils/src/main/java/ghidra/util/database/DBCachedObjectStoreFactory.java:1379

	 * @param cls the class
	 * @param fields a map to receive the fields
	 * @param indexFields a list for receiving fields to be indexed
	 * @param sparseFields a list for receiving fields to have sparse storage
	 */
	private static void collectFields(Class<?> cls, Map<String, Field> fields,
			List<Field> indexFields, List<Field> sparseFields) {
		Class<?> superclass = cls.getSuperclass();
		if (superclass != null) {
			collectFields(superclass, fields, indexFields, sparseFields);
		}
		for (Field f : cls.getDeclaredFields()) {
			DBAnnotatedField annotation = f.getAnnotation(DBAnnotatedField.class);
			if (annotation == null) {
				continue;
			}
			int mod = f.getModifiers();
			if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
				throw new IllegalArgumentException(
					DBAnnotatedField.class.getSimpleName() + " must be non-static and non-final");
			}
			f.setAccessible(true);
			Field old = fields.put(annotation.column(), f);
			if (old != null) {
				indexFields.remove(old);
			}
			if (annotation.indexed()) {
				indexFields.add(f);
			}
			if (annotation.sparse()) {
				sparseFields.add(f);
			}
		}
	}

	private final DBHandle handle;
	private final DBCachedDomainObjectAdapter adapter;

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Remove both `static` and `final` from the annotated field.
  2. If you actually wanted a static column handle, use @DBAnnotatedColumn instead.
  3. Re-run the store open to confirm collection succeeds.

Example fix

// before
@DBAnnotatedField(column="name")
final String name;
// after
@DBAnnotatedField(column="name")
String name;
Defensive patterns

Strategy: validation

Validate before calling

for (Field f : objectType.getDeclaredFields()) {
    DBAnnotatedField a = f.getAnnotation(DBAnnotatedField.class);
    if (a == None) continue;
    int mod = f.getModifiers();
    if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
        throw new IllegalStateException("@DBAnnotatedField must be non-static non-final: " + f);
    }
}

Prevention

When it happens

Trigger: A field annotated @DBAnnotatedField that carries the `static` or `final` modifier (e.g. `@DBAnnotatedField(column="x") final String name;` or a `static` shared field).

Common situations: Marking a field `final` for an immutability style; sharing a value via `static`; generated/Lombok code that adds final; confusing the value field with a static @DBAnnotatedColumn handle.

Related errors


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