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

  1. Inspect the caused-by exception (e.getCause()) to identify which accessor threw and why
  2. Fix the bug in the record accessor or the data it depends on
  3. 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

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


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/cd4d2082d3f78537. Report an issue: GitHub.