{"id":"d4eb7c01a640b4d9","repo":"google/gson","slug":"serializedname-on-methoddescription-is-no","errorCode":null,"errorMessage":"@SerializedName on \" + methodDescription + \" is not supported","messagePattern":"@SerializedName on \" \\+ methodDescription \\+ \" is not supported","errorType":"exception","errorClass":"JsonIOException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java","lineNumber":383,"sourceCode":"          // GsonBuilder.excludeFieldsWithModifiers can overwrite this.\n          if (Modifier.isStatic(field.getModifiers())) {\n            deserialize = false;\n          } else {\n            accessor = ReflectionHelper.getAccessor(raw, field);\n            // If blockInaccessible, skip and perform access check later\n            if (!blockInaccessible) {\n              ReflectionHelper.makeAccessible(accessor);\n            }\n\n            // @SerializedName can be placed on accessor method, but it is not supported there\n            // If field and method have annotation it is not easily possible to determine if\n            // accessor method is implicit and has inherited annotation, or if it is explicitly\n            // declared with custom annotation\n            if (accessor.getAnnotation(SerializedName.class) != null\n                && field.getAnnotation(SerializedName.class) == null) {\n              String methodDescription =\n                  ReflectionHelper.getAccessibleObjectDescription(accessor, false);\n              throw new JsonIOException(\n                  \"@SerializedName on \" + methodDescription + \" is not supported\");\n            }\n          }\n        }\n\n        // If blockInaccessible, skip and perform access check later\n        // For Records if the accessor method is used the field does not have to be made accessible\n        if (!blockInaccessible && accessor == null) {\n          ReflectionHelper.makeAccessible(field);\n        }\n\n        Type fieldType = GsonTypes.resolve(type.getType(), raw, field.getGenericType());\n        List<String> fieldNames = getFieldNames(field);\n        String serializedName = fieldNames.get(0);\n        BoundField boundField =\n            createBoundField(\n                context,\n                field,","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java#L365-L401","documentation":"Thrown during record adapter construction when a record accessor method carries @SerializedName but the backing field does not. Gson cannot reliably distinguish an inherited implicit annotation from an explicit one on the accessor, so it rejects this combination as a JsonIOException. The field must own the annotation.","triggerScenarios":"A developer explicitly annotates a record's accessor method (e.g., `@SerializedName(\"x\") public String name()`) instead of the component field. Detected in getBoundFields (line 379) when accessor.getAnnotation(SerializedName.class) != null && field.getAnnotation(SerializedName.class) == null.","commonSituations":"Migrating POJOs to records and carrying over method-level annotations; tooling/IDE auto-adding annotations to accessors; confusion about whether records accept @SerializedName on accessors.","solutions":["Move @SerializedName from the accessor method to the record component (field).","Remove the annotation from the accessor if the default name is acceptable.","Use a custom TypeAdapter if you genuinely need method-level name control."],"exampleFix":"// before\npublic record Item(@SerializedName(\"i\") int id) {\n  @SerializedName(\"i\") public int id() { return id; }\n}\n\n// after: annotation lives on the component only\npublic record Item(@SerializedName(\"i\") int id) {}","handlingStrategy":"validation","validationCode":"// Reject records whose accessor carries @SerializedName without the field\nfor (Method m : MyRecord.class.getDeclaredMethods()) {\n  if (m.isAnnotationPresent(SerializedName.class)) {\n    Field f = MyRecord.class.getDeclaredField(m.getName());\n    if (!f.isAnnotationPresent(SerializedName.class)) {\n      throw new IllegalStateException(\"Move @SerializedName to component \" + m.getName());\n    }\n  }\n}","typeGuard":"null","tryCatchPattern":"null","preventionTips":["When migrating to records, move all method-level annotations to the components.","Code-review record declarations for stray accessor annotations.","Add a lint/test that builds a Gson instance with every record type to fail fast at startup."],"tags":["gson","record","serializedname","annotation","accessor"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}