prestodb/presto · error · PrestoException

FUNCTION_IMPLEMENTATION_ERROR

FUNCTION_IMPLEMENTATION_ERROR

Error message

When function got no input, it should either produce output or return Blocked state

What it means

FUNCTION_IMPLEMENTATION_ERROR raised by RegularTableFunctionPartition.process when a table function implementation neither produced an output page nor declared itself Blocked despite receiving no input data in this round. Table functions follow a WorkProcessor protocol: each step must yield a result, be blocked, or have consumed input; producing neither violates the contract. This is a bug in the table function's implementation, not user SQL.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/operator/RegularTableFunctionPartition.java:139

            public WorkProcessor.ProcessState<Page> process()
            {
                TableFunctionProcessorState state = tableFunction.process(inputPages);
                boolean functionGotNoData = inputPages == null;
                if (state == FINISHED) {
                    return WorkProcessor.ProcessState.finished();
                }
                if (state instanceof TableFunctionProcessorState.Blocked) {
                    return WorkProcessor.ProcessState.blocked(toListenableFuture(((TableFunctionProcessorState.Blocked) state).getFuture()));
                }
                TableFunctionProcessorState.Processed processed = (TableFunctionProcessorState.Processed) state;
                if (processed.isUsedInput()) {
                    inputPages = prepareInputPages();
                }
                if (processed.getResult() != null) {
                    return WorkProcessor.ProcessState.ofResult(appendPassThroughColumns(processed.getResult()));
                }
                if (functionGotNoData) {
                    throw new PrestoException(FUNCTION_IMPLEMENTATION_ERROR, "When function got no input, it should either produce output or return Blocked state");
                }
                return WorkProcessor.ProcessState.blocked(immediateFuture(null));
            }
        });
    }

    /**
     * Iterate over the partition by page and extract pages for each table function source from the input page.
     * For each source, project the columns required by the table function.
     * If for some source all data in the partition has been consumed, Optional.empty() is returned for that source.
     * It happens when the partition of this source is shorter than the partition of some other source.
     * The overall length of the table function partition is equal to the length of the longest source partition.
     * When all sources are fully consumed, this method returns null.
     * <p>
     * NOTE: There are two types of table function's source semantics: set and row. The two types of sources should be handled
     * by the TableFunctionDataProcessor in different ways. For a source with set semantics, the whole partition can be used for computations,
     * while for a source with row semantics, each row should be processed independently from all other rows.
     * To enforce that behavior, we could pass to the TableFunctionDataProcessor only one row from a table with row semantics.

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Fix the table function implementation so that when no input is present it either produces a result page or returns a blocked state.
  2. Check the processor's contract handling of empty input partitions (set vs rows source semantics).
  3. If using a built-in function, reproduce with a minimal query and file a bug with the function name and plan.
  4. Verify the state machine transitions in processOperator/processed handling conform to WorkProcessor.ProcessState expectations.

Example fix

// before (custom table function)
if (state.isFinished()) { return ProcessState.finished(); }
// never yields result or blocked on empty input
// after
if (state.isFinished()) { return ProcessState.finished(); }
if (noInputProcessed && state.getResult() == null) {
    return ProcessState.blocked(immediateFuture(null));
}
Defensive patterns

Strategy: try-catch

Validate before calling

// When authoring a table function, unit-test the empty-input path:
@Test
void emptyPartitionMustBlockOrEmit() {
    var state = processor.process(emptyInput);
    assertTrue(state.getResult() != null || state.isBlocked(),
        "function with no input must produce output or be blocked");
}

Try / catch

try {
    functionOutput = executeTableFunctionQuery();
} catch (PrestoException e) {
    if ("FUNCTION_IMPLEMENTATION_ERROR".equals(e.getErrorCode().getName())
            && e.getMessage().contains("no input")) {
        throw new IllegalStateException("table function bug; report to function author", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: The function's processor (TableFunctionProcessorState) reports processed data but getResult() is null and functionGotNoData is true — i.e. the processor neither returned a page nor a blocked continuation while no input rows were supplied.

Common situations: Developing a custom TableFunctionDataProcessor that returns finished/processed states inconsistently; changing partitioning or pass-through semantics so the function is invoked with empty partitions; upgrading Presto where the processor state contract became stricter.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/5c40e706ccee4cb9. Report an issue: GitHub.