{"record":{"id":"d45749a8b561810b","repo":"prestodb/presto","slug":"function-implementation-error-d45749","errorCode":"FUNCTION_IMPLEMENTATION_ERROR","errorMessage":"Method %s does not return valid MethodHandle","messagePattern":"Method (.+?) does not return valid MethodHandle","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"error","filePath":"presto-main-base/src/main/java/com/facebook/presto/operator/scalar/annotations/CodegenScalarFromAnnotationsParser.java","lineNumber":147,"sourceCode":"                ImmutableList.of(),\n                parseTypeSignature(method.getAnnotation(SqlType.class).value()),\n                Arrays.stream(method.getParameters()).map(p -> parseTypeSignature(p.getAnnotation(SqlType.class).value())).collect(toImmutableList()),\n                false);\n\n        ComplexTypeFunctionDescriptor descriptor = parseAndCheckFunctionDescriptor(method, signature);\n\n        return new SqlScalarFunction(signature)\n        {\n            @Override\n            public BuiltInScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, FunctionAndTypeManager functionAndTypeManager)\n            {\n                Signature boundSignature = applyBoundVariables(signature, boundVariables, arity);\n                MethodHandle handle;\n                try {\n                    handle = (MethodHandle) method.invoke(null, boundSignature.getArgumentTypes().stream().map(t -> functionAndTypeManager.getType(t)).toArray());\n                }\n                catch (Exception e) {\n                    throw new PrestoException(FUNCTION_IMPLEMENTATION_ERROR, format(\"Method %s does not return valid MethodHandle\", method), e);\n                }\n                return new BuiltInScalarFunctionImplementation(\n                        method.getAnnotation(SqlNullable.class) != null,\n                        getArgumentProperties(method),\n                        handle,\n                        Optional.empty());\n            }\n\n            @Override\n            public SqlFunctionVisibility getVisibility()\n            {\n                return codegenScalarFunction.visibility();\n            }\n\n            @Override\n            public boolean isDeterministic()\n            {\n                return codegenScalarFunction.deterministic();","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main-base/src/main/java/com/facebook/presto/operator/scalar/annotations/CodegenScalarFromAnnotationsParser.java#L129-L165","documentation":"When specializing a @ScalarFunction whose implementation method returns a MethodHandle (the codegen style used by built-in function parsers), Presto reflectively invokes the static method to obtain the handle. If the reflective invocation throws for any reason — wrong argument types, an exception inside the method, class-loading failure — the parser wraps it in FUNCTION_IMPLEMENTATION_ERROR saying the method did not return a valid MethodHandle. This is a plugin/function-authoring error, not a query-input error.","triggerScenarios":"Registering a scalar function whose generate-* static method has a signature mismatch with the declared @TypeParameter/@SqlType annotations, throws internally, or cannot be invoked with the resolved Type array (e.g. wrong arity or incompatible types).","commonSituations":"Custom connector/function plugin development; upgrading Presto where the MethodHandle-based function API changed; typos in @TypeParameter names so bound types don't match the method parameters.","solutions":["Check the plugin/server log for the cause chain (the PrestoException carries the original exception).","Verify the static method signature matches the declared argument types and arity exactly.","Confirm the method is public, static, and returns java.lang.invoke.MethodHandle.","Rebuild the plugin against the exact Presto version in use to fix API drift.","Test function registration in isolation (a minimal plugin) to pinpoint the failing method."],"exampleFix":"// before (mismatched arity)\npublic static MethodHandle lessThan(Type type) { ... }\n// after\npublic static MethodHandle lessThan(Type leftType, Type rightType) { ... }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"// pre-check before registering\nClass<?> c = method.getReturnType();\nboolean valid = java.lang.reflect.Modifier.isStatic(method.getModifiers())\n    && MethodHandle.class.isAssignableFrom(c);","tryCatchPattern":"try { registry.registerFunctions(ScalarFunction.class, classLoader); } catch (PrestoException e) { if (e.getErrorCode().getName().equals(\"FUNCTION_IMPLEMENTATION_ERROR\")) { log.error(\"Bad MethodHandle function: \" + e.getCause(), e); throw e; } }","preventionTips":["Keep generate-* methods public, static, and MethodHandle-returning.","Keep MethodHandle factory signatures in sync with @TypeParameter/@SqlType declarations.","Run plugin registration tests in CI against the target Presto version.","Read the cause chain in server logs to find the underlying reflective failure."],"tags":["presto","function-implementation","reflection","methodhandle","plugin"],"backgroundTag":"function-implementation-error","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}