google/gson · error · JsonIOException
Accessor ${accessorDescription} threw exception
Error message
Accessor ${accessorDescription} threw exception What it means
When serializing a Java record, Gson reads each component value by invoking its accessor method via reflection (accessor.invoke(source)). If the accessor throws, Gson catches the InvocationTargetException and rethrows a JsonIOException wrapping the original cause. This is a failure inside your record's own accessor logic, not a Gson configuration issue.
Source
Thrown at gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java:240
void write(JsonWriter writer, Object source) throws IOException, IllegalAccessException {
if (blockInaccessible) {
if (accessor == null) {
checkAccessible(source, field);
} else {
// Note: This check might actually be redundant because access check for canonical
// constructor should have failed already
checkAccessible(source, accessor);
}
}
Object fieldValue;
if (accessor != null) {
try {
fieldValue = accessor.invoke(source);
} catch (InvocationTargetException e) {
String accessorDescription =
ReflectionHelper.getAccessibleObjectDescription(accessor, false);
throw new JsonIOException(
"Accessor " + accessorDescription + " threw exception", e.getCause());
}
} else {
fieldValue = field.get(source);
}
@SuppressWarnings("ReferenceEquality")
boolean isSameObject = fieldValue == source;
if (isSameObject) {
// avoid direct recursion
return;
}
writer.name(serializedName);
writeTypeAdapter.write(writer, fieldValue);
}
@Override
void readIntoArray(JsonReader reader, int index, Object[] target)View on GitHub (pinned to 310ac341f2)
Solutions
- Inspect the caused-by exception (e.getCause()) to identify which accessor threw and why
- Fix the bug in the record accessor or the data it depends on
- Register a custom JsonSerializer<T> for the record type to bypass the accessor entirely during serialization
Example fix
// before
record User(String name) {
@Override public String name() { return name.toUpperCase(); } // NPE if name is null
}
gson.toJson(new User(null)); // throws
// after
record User(String name) {
@Override public String name() { return name == null ? null : name.toUpperCase(); }
} Defensive patterns
Strategy: try-catch
Try / catch
try {
String json = gson.toJson(recordInstance);
} catch (JsonIOException e) {
// e.getCause() holds the accessor's original exception
Throwable cause = e.getCause();
log.error("Record accessor failed during serialization: {}", cause.getMessage(), cause);
// fall back to a custom serializer or omit the field
} Prevention
- Ensure record accessor methods (including overrides) never throw for null components
- Guard accessor logic with null checks before dereferencing
- Register a custom JsonSerializer for records whose accessors have complex logic
When it happens
Trigger: Calling gson.toJson(recordInstance) where the record overrides an accessor method (or the implicit accessor dereferences a null component) and that method throws a NullPointerException, IllegalStateException, or any other runtime exception.
Common situations: A record accessor that performs validation or lazy computation fails on bad state; a record component is null and the accessor calls a method on it; migrating a class with getter logic to a record where the accessor now throws.
Related errors
- ReflectionAccessFilter does not permit using reflection for
- Could not find the index in the constructor '${constructor}'
- cannot serialize ${srcType.getName()}; did you forget to reg
- cannot serialize ${srcType.getName()} because it already def
- GSON (${GsonBuildConfig.VERSION}) cannot handle ${type}
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/cd4d2082d3f78537.
Report an issue: GitHub.