pappmiku opened a new issue, #14931: URL: https://github.com/apache/grails-core/issues/14931
### Expected Behavior When a controller attempts to respond with an object implementing `grails.validation.Validateable`, validation objects should not be serialized (possibly with the exception of the `errors` field, which was the case in Grails 6.2.3, but I assume no actual validation was being performed). ### Actual Behaviour Attempting to respond with a validateable object results in incorrect serialization and/or an exception. It seems that non-string fields cause exceptions (see below; id is a Long), with them removed the serialization passes, but there are two extra fields - `constraints` and `constraintsMap` - included in the resulting json. Is this an intended behavior going forward? I know it doesn't necessarily make sense to even have validateable response dtos, but this is the code I have to work with. 😄 Though a lot of them are used as both requests and responses... <details> <summary>Stacktrace:</summary> ``` java.lang.reflect.InvocationTargetException: null at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:210) at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:187) at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:108) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) at org.eclipse.jetty.ee10.servlet.ServletHolder.handle(ServletHolder.java:736) at org.eclipse.jetty.ee10.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1622) at org.eclipse.jetty.ee10.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:195) at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205) at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:80) at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205) at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:70) at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205) at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205) at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205) at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205) at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594) at org.eclipse.jetty.ee10.servlet.ServletHandler$MappedServlet.handle(ServletHandler.java:1555) at org.eclipse.jetty.ee10.servlet.ServletChannel.dispatch(ServletChannel.java:823) at org.eclipse.jetty.ee10.servlet.ServletChannel.handle(ServletChannel.java:440) at org.eclipse.jetty.ee10.servlet.ServletHandler.handle(ServletHandler.java:470) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:575) at org.eclipse.jetty.ee10.servlet.SessionHandler.handle(SessionHandler.java:717) at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1071) at org.eclipse.jetty.server.Handler$Wrapper.handle(Handler.java:740) at org.eclipse.jetty.server.handler.EventsHandler.handle(EventsHandler.java:81) at org.eclipse.jetty.server.Server.handle(Server.java:182) at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:678) at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:416) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:99) at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:981) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1211) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1166) at java.base/java.lang.Thread.run(Thread.java:840) Caused by: org.grails.web.converters.exceptions.ConverterException: Error converting Bean with class grails.validation.ConstrainedDelegate at org.grails.web.converters.marshaller.json.GroovyBeanMarshaller.marshalObject(GroovyBeanMarshaller.java:88) at org.grails.web.converters.marshaller.json.GroovyBeanMarshaller.marshalObject(GroovyBeanMarshaller.java:42) at grails.converters.JSON.value(JSON.java:187) at grails.converters.JSON.convertAnother(JSON.java:147) at org.grails.web.converters.marshaller.json.MapMarshaller.marshalObject(MapMarshaller.java:48) at org.grails.web.converters.marshaller.json.MapMarshaller.marshalObject(MapMarshaller.java:33) at grails.converters.JSON.value(JSON.java:187) at grails.converters.JSON.convertAnother(JSON.java:147) at org.grails.web.converters.marshaller.json.GroovyBeanMarshaller.marshalObject(GroovyBeanMarshaller.java:70) at org.grails.web.converters.marshaller.json.GroovyBeanMarshaller.marshalObject(GroovyBeanMarshaller.java:42) at grails.converters.JSON.value(JSON.java:187) at grails.converters.JSON.render(JSON.java:122) at org.grails.plugins.web.rest.render.json.DefaultJsonRenderer.renderJson(DefaultJsonRenderer.groovy:119) at org.grails.plugins.web.rest.render.json.DefaultJsonRenderer.renderJson(DefaultJsonRenderer.groovy:113) at org.grails.plugins.web.rest.render.json.DefaultJsonRenderer.render(DefaultJsonRenderer.groovy:94) at grails.artefact.controller.RestResponder$Trait$Helper.internalRespond(RestResponder.groovy:218) at grails.artefact.controller.RestResponder$Trait$Helper.respond(RestResponder.groovy:124) at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321) at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321) at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321) at com.example.TestController.testFail(TestController.groovy:14) ... 46 common frames omitted Caused by: java.lang.reflect.InvocationTargetException: null at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at org.grails.web.converters.marshaller.json.GroovyBeanMarshaller.marshalObject(GroovyBeanMarshaller.java:68) ... 66 common frames omitted Caused by: groovy.lang.MissingPropertyException: CreditCard constraint only applies to a String property at grails.gorm.validation.DefaultConstrainedProperty.isCreditCard(DefaultConstrainedProperty.groovy:403) at grails.validation.ConstrainedDelegate.isCreditCard(ConstrainedDelegate.groovy:123) ... 69 common frames omitted ``` </details> <details> <summary>JSON generated in Grails 6.2.3:</summary> ```json { "companies" : [ { "errors" : { "errors" : [ ] }, "id" : 1, "info" : "", "name" : "." }, { "errors" : { "errors" : [ ] }, "id" : 2, "info" : "", "name" : "" } ], "timestamp" : "2025-07-22T13:15:42.81+02:00" } ``` </details> <details> <summary>JSON generated in Grails 7:</summary> ```json { "companies" : [ { "constraints" : { "delegate" : "cz.actis.exevido.admin.CompanyDTO", "directive" : 0, "maximumNumberOfParameters" : 1, "owner" : "cz.actis.exevido.admin.CompanyDTO", "parameterTypes" : [ "java.lang.Object" ], "resolveStrategy" : 0, "thisObject" : "cz.actis.exevido.admin.CompanyDTO" }, "constraintsMap" : { "name" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "name", "valid" : true }, { "blank" : true, "name" : "blank", "parameter" : true, "propertyName" : "name", "valid" : true } ], "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 2, "owner" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "property" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "name", "valid" : true }, { "blank" : true, "name" : "blank", "parameter" : true, "propertyName" : "name", "valid" : true } ], "attributes" : { }, "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "metaConstraints" : { }, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 2, "owner" : "cz.actis.exevido.admin.CompanyDTO", "owningClass" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "propertyName" : "name", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null }, "propertyName" : "name", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null }, "info" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "info", "valid" : true }, { "blank" : true, "name" : "blank", "parameter" : true, "propertyName" : "info", "valid" : true } ], "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 3, "owner" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "property" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "info", "valid" : true }, { "blank" : true, "name" : "blank", "parameter" : true, "propertyName" : "info", "valid" : true } ], "attributes" : { }, "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "metaConstraints" : { }, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 3, "owner" : "cz.actis.exevido.admin.CompanyDTO", "owningClass" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "propertyName" : "info", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null }, "propertyName" : "info", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null }, "id" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "id", "valid" : true } ], "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 4, "owner" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "property" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "id", "valid" : true } ], "attributes" : { }, "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "metaConstraints" : { }, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 4, "owner" : "cz.actis.exevido.admin.CompanyDTO", "owningClass" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "propertyName" : "id", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null }, "propertyName" : "id", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null } }, "errors" : { "errors" : [ ] }, "id" : "", "info" : "", "name" : "" }, { "constraints" : { "delegate" : "cz.actis.exevido.admin.CompanyDTO", "directive" : 0, "maximumNumberOfParameters" : 1, "owner" : "cz.actis.exevido.admin.CompanyDTO", "parameterTypes" : [ "java.lang.Object" ], "resolveStrategy" : 0, "thisObject" : "cz.actis.exevido.admin.CompanyDTO" }, "constraintsMap" : { "name" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "name", "valid" : true }, { "blank" : true, "name" : "blank", "parameter" : true, "propertyName" : "name", "valid" : true } ], "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 2, "owner" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "property" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "name", "valid" : true }, { "blank" : true, "name" : "blank", "parameter" : true, "propertyName" : "name", "valid" : true } ], "attributes" : { }, "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "metaConstraints" : { }, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 2, "owner" : "cz.actis.exevido.admin.CompanyDTO", "owningClass" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "propertyName" : "name", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null }, "propertyName" : "name", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null }, "info" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "info", "valid" : true }, { "blank" : true, "name" : "blank", "parameter" : true, "propertyName" : "info", "valid" : true } ], "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 3, "owner" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "property" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "info", "valid" : true }, { "blank" : true, "name" : "blank", "parameter" : true, "propertyName" : "info", "valid" : true } ], "attributes" : { }, "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "metaConstraints" : { }, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 3, "owner" : "cz.actis.exevido.admin.CompanyDTO", "owningClass" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "propertyName" : "info", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null }, "propertyName" : "info", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null }, "id" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "id", "valid" : true } ], "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 4, "owner" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "property" : { "appliedConstraints" : [ { "name" : "nullable", "nullable" : true, "parameter" : true, "propertyName" : "id", "valid" : true } ], "attributes" : { }, "blank" : true, "creditCard" : false, "display" : true, "editable" : true, "email" : false, "format" : null, "inList" : null, "matches" : null, "max" : null, "maxSize" : null, "metaConstraints" : { }, "min" : null, "minSize" : null, "notEqual" : null, "nullable" : true, "order" : 4, "owner" : "cz.actis.exevido.admin.CompanyDTO", "owningClass" : "cz.actis.exevido.admin.CompanyDTO", "password" : false, "propertyName" : "id", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null }, "propertyName" : "id", "propertyType" : "java.lang.String", "range" : null, "scale" : null, "size" : null, "url" : false, "widget" : null } }, "errors" : { "errors" : [ ] }, "id" : "", "info" : "", "name" : "" } ], "timestamp" : "2025-07-22T13:04:18.065+02:00" } ``` </details> <details> <summary>Frontend call response:</summary> <img width="536" height="502" alt="Image" src="https://github.com/user-attachments/assets/4a64d8c9-bd6a-4af5-a8da-76a364826353" /> </details> ### Steps To Reproduce I've prepared a very simple example from a Forge generated app, see below. Unfortunately I'm unable to replicate the ability to get the "bad" serialized dto json there, so above I've attached examples from the project I'm working on. 1. start the application 2. go to localhost:8080/api/test/ok - the dto is returned as expected 3. go to localhost:8080/api/test/fail - serialization fails with "Error converting Bean with class grails.validation.ConstrainedDelegate - CreditCard constraint only applies to a String property" ### Environment Information - OS: Manjaro Linux 6.15.6-arch1-1.1-g14 - JDK: 17.0.15-tem ### Example Application https://github.com/pappmiku/grails-7-ser-ex ### Version 7.0.0-SNAPSHOT -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@grails.apache.org.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org