{"record":{"id":"3b6d876887e63219","repo":"baomidou/mybatis-plus","slug":"unsupported-proxy-type-for-groovy-lambda-extractio","errorCode":null,"errorMessage":"Unsupported proxy type for Groovy lambda extraction: {}. Expected a Groovy ConvertedClosure handler exposing getDelegate().","messagePattern":"Unsupported proxy type for Groovy lambda extraction: (.+?)\\. Expected a Groovy ConvertedClosure handler exposing getDelegate\\(\\)\\.","errorType":"exception","errorClass":"MybatisPlusException","httpStatus":null,"severity":"error","filePath":"mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/GroovyLambdaMeta.java","lineNumber":50,"sourceCode":" * @since 3.5.12\n */\npublic class GroovyLambdaMeta implements LambdaMeta {\n\n    private final Class<?> clazz;\n    private final String name;\n\n    public GroovyLambdaMeta(Proxy func) {\n        InvocationHandler handler = Proxy.getInvocationHandler(func);\n        try {\n            Method getDelegate = handler.getClass().getMethod(\"getDelegate\");\n            Object delegate = getDelegate.invoke(handler);\n            Method getMethod = delegate.getClass().getMethod(\"getMethod\");\n            this.name = (String) getMethod.invoke(delegate);\n            Method getOwner = delegate.getClass().getMethod(\"getOwner\");\n            Object owner = getOwner.invoke(delegate);\n            this.clazz = (owner instanceof Class) ? (Class<?>) owner : owner.getClass();\n        } catch (NoSuchMethodException e) {\n            throw new MybatisPlusException(\"Unsupported proxy type for Groovy lambda extraction: \"\n                + handler.getClass().getName()\n                + \". Expected a Groovy ConvertedClosure handler exposing getDelegate().\", e);\n        } catch (Exception e) {\n            throw new MybatisPlusException(\"Failed to extract Groovy lambda meta from proxy handler: \"\n                + handler.getClass().getName(), e);\n        }\n    }\n\n    @Override\n    public String getImplMethodName() {\n        return name;\n    }\n\n    @Override\n    public Class<?> getInstantiatedClass() {\n        return clazz;\n    }\n","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/baomidou/mybatis-plus/blob/bf67d907478c724120bf76292da54abf9e73c2b3/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/GroovyLambdaMeta.java#L32-L68","documentation":"GroovyLambdaMeta extracts the instantiated method/owner from a Groovy closure used as a lambda (e.g. in QueryWrapper lambdas under Groovy). It expects the closure to be a JDK Proxy whose InvocationHandler is a Groovy ConvertedClosure exposing getDelegate(). If handler.getClass().getMethod(\"getDelegate\") throws NoSuchMethodException, mybatis-plus reports MybatisPlusException('Unsupported proxy type for Groovy lambda extraction: <handler class>').","triggerScenarios":"Using lambda QueryWrapper/LambdaQueryWrapper syntax inside Groovy code where the 'lambda' the framework receives is a plain Groovy Closure wrapped by a proxy whose invocation handler is not ConvertedClosure (or a different Groovy version's internal layout). The reflective contract on Groovy internals no longer holds.","commonSituations":"Groovy version upgrades that change closure proxy internals (MethodClosure vs ConvertedClosure handling); Grape/Groovy 4 vs 3 differences; passing a method reference or custom proxy where a true ConvertedClosure is expected; non-Groovy proxies routed into the Groovy branch of lambda resolution.","solutions":["Align Groovy versions with what the mybatis-plus release supports (check the release notes for Groovy support matrix).","Avoid Groovy closure syntax in wrapper APIs: use the string/column-name wrappers (QueryWrapper.eq(\"col\", val)) or explicit SFunction from Java.","If you build custom proxies around closures, ensure the handler exposes getDelegate()/getMethod()/getOwner() like ConvertedClosure does.","Upgrade mybatis-plus — Groovy lambda extraction robustness improved across 3.5.x patches."],"exampleFix":"// before (Groovy): closure routed to GroovyLambdaMeta with unsupported handler\nwrapper.eq({ it.name }, 'bob') // may explode on Groovy internals\n\n// after: use column-name wrappers under Groovy\nwrapper.eq('name', 'bob')","handlingStrategy":"validation","validationCode":"Object handler = Proxy.getInvocationHandler(func);\nboolean groovyConvertible = false;\ntry {\n    handler.getClass().getMethod(\"getDelegate\");\n    groovyConvertible = true;\n} catch (NoSuchMethodException ignored) { }\nif (!groovyConvertible) { /* use column-name wrappers instead */ }","typeGuard":"static boolean isGroovyConvertedClosure(Object func) {\n    if (!(func instanceof Proxy)) return false;\n    try {\n        Proxy.getInvocationHandler(func).getClass().getMethod(\"getDelegate\");\n        return true;\n    } catch (NoSuchMethodException e) {\n        return false;\n    }\n}","tryCatchPattern":"try {\n    wrapper.eq(SFunctionCast.toSFunction(closure), value);\n} catch (MybatisPlusException e) {\n    // fall back to column-name condition when Groovy lambda extraction is unsupported\n    wrapper.eq(\"column_name\", value);\n}","preventionTips":["In Groovy code, prefer string-column wrapper conditions over closure syntax.","Keep Groovy and mybatis-plus versions aligned with the compatibility matrix."],"tags":["groovy","lambda","reflection","version-compat"],"backgroundTag":null,"analyzedSha":"bf67d907478c724120bf76292da54abf9e73c2b3","analyzedAt":"2026-08-14T15:17:09.543Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}