🔴 The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
// DOC-DERIVED — message carries only the missing field name
Exception in thread "main" java.lang.NoSuchFieldException: userName
at java.base/java.lang.Class.getDeclaredField(Class.java:2792)
at com.devinhyderabad.mapping.RowMapper.bind(RowMapper.java:22)⚡ Quick Fix Works 80% of the time
Walk up the superclass chain until the declared field appears, then setAccessible(true) before reading.
static Field findInherited(Class<?> type, String name)
throws NoSuchFieldException {
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
try {
Field f = c.getDeclaredField(name);
f.setAccessible(true);
return f;
} catch (NoSuchFieldException ignored) { }
}
throw new NoSuchFieldException(name);
}🧠 Why this Happens
Tap to expand the deep technical explanation
Two lookups exist and they search differently: getDeclaredField inspects ONLY the declaring class itself — nothing inherited — while getField scans public fields up the hierarchy but stops at anything non-public. A private superclass field hides from both unless you climb getSuperclass() manually, and access afterwards still obeys modifiers, hence setAccessible. Critically, this checked exception is thrown by the reflection LOOKUP API when you ask for a field. NoSuchFieldError is different machinery entirely: the JVM throws it at ordinary compiled GETFIELD/PUTFIELD instructions when the runtime class lacks a field some other class was compiled against — jar skew, not lookup mistakes. Same words, opposite layers: Exception means your reflection query missed; Error means somebody's binary broke.
The HITEC City Parking Spot Analogy:
getDeclaredField opens exactly ONE drawer of the filing cabinet. The document filed upstairs in the parent cabinet was never in this drawer — and NoSuchFieldError would be the cabinet having been replaced overnight with a smaller one.
🔁 How to Reproduce Confirm this is your error
Declare Base { private String hidden; }, extend it as Sub, then call Sub.class.getDeclaredField("hidden") — throws; Base.class.getDeclaredField("hidden") — succeeds. DOC-DERIVED (lookup rules stable since 1.1).
🛠️ Solutions (5 Ways to Fix)
Walk the superclass chain for declared fields
👉 Use this when/if mapping or serialization code must read private state declared in a parent class.
Loop getSuperclass() calling getDeclaredField at each level until it stops throwing, then setAccessible(true). This is the only reliable way to reach non-public inherited fields, and centralizing it prevents copy-pasted half-implementations.
static Field findInherited(Class<?> type, String name)
throws NoSuchFieldException {
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
try {
Field f = c.getDeclaredField(name);
f.setAccessible(true);
return f;
} catch (NoSuchFieldException ignored) { }
}
throw new NoSuchFieldException(name);
}Choose getField vs getDeclaredField deliberately
👉 Use this when/if you control the visibility contract and simply picked the wrong lookup.
getField = public only, includes inherited statics and constants — perfect for public constants, useless elsewhere. getDeclaredField = everything declared in exactly that class, nothing inherited. Matching scope to need deletes most occurrences of this exception.
Field MAX = Limit.class.getField("MAX"); // public, inherited OK
Field cache = Cache.class.getDeclaredField("cache"); // declared-only
cache.setAccessible(true); // non-public needs thisCache resolved Fields in a ConcurrentHashMap
👉 Use this when/if the same lookup repeats per row/request and both latency and auditability matter.
Resolution is the expensive, throwable part; get/set on a cached Field is cheap. One concurrent map keyed by class+field name gives you a single audited place where setAccessible decisions live and failures surface exactly once.
private static final ConcurrentMap<String, Field> FIELDS =
new ConcurrentHashMap<>();
static Field cached(Class<?> type, String name) {
return FIELDS.computeIfAbsent(type.getName() + "#" + name,
k -> findInheritedUnchecked(type, name));
}On records, read components and accessors — not fields
👉 Use this when/if reflection hits record classes and field-based tricks stop working.
Record fields are final and refuse setAccessible(true), so old field-writing mappers explode. The supported route is getRecordComponents() paired with accessor Method handles — same data, sanctioned API, no exception.
for (RecordComponent rc : Point.class.getRecordComponents()) {
Method accessor = Point.class.getMethod(rc.getName());
Object value = accessor.invoke(point);
}Recognize when it is really NoSuchFieldError — align versions
👉 Use this when/if the failure comes from ordinary compiled code, not your reflection line.
NoSuchFieldError at a normal statement means a compiled caller references a field the runtime jar lacks — dependency skew, not lookup bugs. Run dependency convergence, align the library versions across modules, and rebuild. Never confuse it with the reflection Exception.
mvn enforcer:enforce -Drules=dependencyConvergence
# or gradle: config.all { resolutionStrategy.failOnVersionConflict() }📋 Version Notes
Lookup scopes as described; setAccessible(true) unblocks non-public reads everywhere.
Module access adds a gate AFTER successful lookup — opens matter even when resolution succeeds.
Records arrive: their final fields reject setAccessible(true) — RecordComponent API is the supported route.
Unchanged; the Error twin (NoSuchFieldError) follows its own linkage rules — never conflate the two.
🛡️ How to Prevent This Next Time
Model shared state in the class that owns it, avoid frameworks that deep-reflect private inherited fields without saying so, prefer accessors and method handles over cross-class field reads, and enforce version convergence so field removals cannot ambush compiled callers.