{"id":"ff04c98e1df6ae71","repo":"google/gson","slug":"accessor-accessordescription-threw-excepti","errorCode":null,"errorMessage":"Accessor \" + accessorDescription + \" threw exception","messagePattern":"Accessor \" \\+ accessorDescription \\+ \" threw exception","errorType":"exception","errorClass":"JsonIOException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java","lineNumber":240,"sourceCode":"      void write(JsonWriter writer, Object source) throws IOException, IllegalAccessException {\n        if (blockInaccessible) {\n          if (accessor == null) {\n            checkAccessible(source, field);\n          } else {\n            // Note: This check might actually be redundant because access check for canonical\n            // constructor should have failed already\n            checkAccessible(source, accessor);\n          }\n        }\n\n        Object fieldValue;\n        if (accessor != null) {\n          try {\n            fieldValue = accessor.invoke(source);\n          } catch (InvocationTargetException e) {\n            String accessorDescription =\n                ReflectionHelper.getAccessibleObjectDescription(accessor, false);\n            throw new JsonIOException(\n                \"Accessor \" + accessorDescription + \" threw exception\", e.getCause());\n          }\n        } else {\n          fieldValue = field.get(source);\n        }\n\n        @SuppressWarnings(\"ReferenceEquality\")\n        boolean isSameObject = fieldValue == source;\n        if (isSameObject) {\n          // avoid direct recursion\n          return;\n        }\n        writer.name(serializedName);\n        writeTypeAdapter.write(writer, fieldValue);\n      }\n\n      @Override\n      void readIntoArray(JsonReader reader, int index, Object[] target)","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java#L222-L258","documentation":"Thrown during serialization of a record when its accessor method (the implicitly or explicitly declared component accessor) throws an exception. Gson catches InvocationTargetException and rethrows a JsonIOException wrapping the original cause (e.getCause()). The failure originates in user code on the record, not in Gson internals.","triggerScenarios":"A record's accessor method throws because it contains custom logic, is annotated with validation that fails, lazily computes a value that errors, or its state is inconsistent at serialization time. Triggered in BoundField.write (ReflectiveTypeAdapterFactory.java:236) when accessor.invoke(source) raises InvocationTargetException.","commonSituations":"Records with accessor overrides that delegate to services not initialized at serialization time; records wrapping nullable fields whose accessor does unguarded .get(); records produced by deserialization with null components then re-serialized through a validating accessor; concurrent mutation of a record's components mid-serialize.","solutions":["Inspect the wrapped cause (e.getCause()) in the JsonIOException to find the real failure in the accessor.","Fix the bug or defensive logic inside the record accessor method.","Register a JsonSerializer for the record type so Gson does not invoke the accessor.","Ensure the record instance is in a consistent, fully-initialized state before calling gson.toJson."],"exampleFix":"// before\npublic record Money(long cents) {\n  public Money { if (cents < 0) throw new IllegalArgumentException(); }\n  public long cents() { return Math.toIntExact(cents); } // throws on overflow\n}\ngson.toJson(new Money(Long.MAX_VALUE));\n\n// after: remove lossy accessor, keep domain check\npublic long cents() { return cents; }","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"// Guard before serializing a record: invoke its accessors once\nstatic <T> void checkRecordAccessors(T value) throws Throwable {\n  for (Method m : value.getClass().getMethods()) {\n    if (m.getParameterCount()==0 && m.getDeclaringClass()==value.getClass()) {\n      try { m.invoke(value); }\n      catch (InvocationTargetException e) { throw e.getCause(); }\n    }\n  }\n}","tryCatchPattern":"try {\n  gson.toJson(record);\n} catch (JsonIOException e) {\n  Throwable cause = e.getCause();\n  // handle the real failure thrown by the accessor\n  log.error(\"record accessor failed\", cause);\n}","preventionTips":["Keep record accessors pure: no validation that can fail on already-valid state.","Unit-test record serialization with the full range of values you expect to round-trip.","Avoid delegating from accessors to services or external state.","Register a JsonSerializer for records whose accessors are non-trivial."],"tags":["gson","record","serialization","accessor","invocation-target"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}