{"record":{"id":"653ac7bb1ded1dc2","repo":"prestodb/presto","slug":"function-implementation-error-653ac7","errorCode":"FUNCTION_IMPLEMENTATION_ERROR","errorMessage":"When function got no input, it should either produce output or return Blocked state","messagePattern":"When function got no input, it should either produce output or return Blocked state","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"error","filePath":"presto-main-base/src/main/java/com/facebook/presto/operator/EmptyTableFunctionPartition.java","lineNumber":71,"sourceCode":"        this.passThroughTypes = passThroughTypes.toArray(new Type[] {});\n    }\n\n    @Override\n    public WorkProcessor<Page> toOutputPages()\n    {\n        return WorkProcessor.create(() -> {\n            TableFunctionProcessorState state = tableFunction.process(null);\n            if (state == FINISHED) {\n                return WorkProcessor.ProcessState.finished();\n            }\n            if (state instanceof TableFunctionProcessorState.Blocked) {\n                return WorkProcessor.ProcessState.blocked(toListenableFuture(((TableFunctionProcessorState.Blocked) state).getFuture()));\n            }\n            TableFunctionProcessorState.Processed processed = (TableFunctionProcessorState.Processed) state;\n            if (processed.getResult() != null) {\n                return WorkProcessor.ProcessState.ofResult(appendNullsForPassThroughColumns(processed.getResult()));\n            }\n            throw new PrestoException(FUNCTION_IMPLEMENTATION_ERROR, \"When function got no input, it should either produce output or return Blocked state\");\n        });\n    }\n\n    private Page appendNullsForPassThroughColumns(Page page)\n    {\n        if (page.getChannelCount() != properChannelsCount + passThroughSourcesCount) {\n            throw new PrestoException(\n                    FUNCTION_IMPLEMENTATION_ERROR,\n                    format(\n                            \"Table function returned a page containing %s channels. Expected channel number: %s (%s proper columns, %s pass-through index columns)\",\n                            page.getChannelCount(),\n                            properChannelsCount + passThroughSourcesCount,\n                            properChannelsCount,\n                            passThroughSourcesCount));\n        }\n\n        Block[] resultBlocks = new Block[properChannelsCount + passThroughTypes.length];\n","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main-base/src/main/java/com/facebook/presto/operator/EmptyTableFunctionPartition.java#L53-L89","documentation":"EmptyTableFunctionPartition drives a table function over partitions. The TableFunctionProcessorState contract requires that when the processor receives no input, it must either return an output page or a Blocked state. If a Processed state with a null result is produced with no input, the library throws FUNCTION_IMPLEMENTATION_ERROR — this is a bug in the table function implementation, not the query.","triggerScenarios":"A custom TableFunctionProcessor returning TableFunctionProcessorState.Processed with a null result when called with an empty/no-input partition, inside EmptyTableFunctionPartition.toOutputPages.","commonSituations":"Third-party or newly written table functions that mishandle the empty-partition case; upstream Presto bugs in a table function fixed in later versions.","solutions":["Fix the table function processor to return Processed.nonBlocking(/* page */) with output or TableFunctionProcessorState.blocked(future) when input is empty","Upgrade Presto / the connector providing the table function to a version with the empty-input bug fixed","Avoid the table function on empty partitions (guard the query) as a workaround"],"exampleFix":"// before\nif (!hasInput) {\n    return Processed.nonBlocking(null); // INVALID\n}\n// after\nif (!hasInput) {\n    return Processed.nonBlocking(buildEmptyOutputPage()); // or blocked(future)\n}","handlingStrategy":"try-catch","validationCode":"// for table function authors: verify empty-input behavior in a unit test\nTableFunctionProcessorState s = processor.process(new Page(0));\ncheckState(!(s instanceof Processed) || ((Processed) s).getResult() != null,\n    \"empty input must yield output or Blocked\");","typeGuard":null,"tryCatchPattern":"try {\n    return tableFunctionPartition.toOutputPages();\n} catch (PrestoException e) {\n    if (e.getErrorCode().getName().equals(\"FUNCTION_IMPLEMENTATION_ERROR\")) {\n        // treat as connector bug: fail query with function name context or skip partition\n    }\n    throw e;\n}","preventionTips":["Table function authors: always return an output page or Blocked when input is empty","Add empty-partition tests to every TableFunctionProcessor","Keep Presto/connector versions patched for known table function bugs"],"tags":["presto","table-function","contract-violation","function-implementation"],"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"}