diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 359aafea681..92a7be09dd3 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -143,6 +143,7 @@ typedef struct SQLFunctionCache
 {
 	SQLFunctionHashEntry *func; /* associated SQLFunctionHashEntry */
 
+	bool		active;			/* are we executing this cache entry? */
 	bool		lazyEvalOK;		/* true if lazyEval is safe */
 	bool		shutdown_reg;	/* true if registered shutdown callback */
 	bool		lazyEval;		/* true if using lazyEval for result query */
@@ -556,6 +557,22 @@ init_sql_fcache(FunctionCallInfo fcinfo, bool lazyEvalOK)
 		finfo->fn_extra = fcache;
 	}
 
+	/*
+	 * If the SQLFunctionCache is marked as active, we must have errored out
+	 * of a prior execution.  Reset state.  (It might seem that we could also
+	 * reach this during recursive invocation of a SQL function, but we won't
+	 * because that case won't involve re-use of the same FmgrInfo.)
+	 *
+	 * In particular, we must clear fcache->cplan without doing
+	 * ReleaseCachedPlan, because error cleanup from the prior execution would
+	 * have taken care of releasing that plan.
+	 */
+	if (fcache->active)
+	{
+		fcache->cplan = NULL;
+		fcache->eslist = NULL;
+	}
+
 	/*
 	 * If we are resuming execution of a set-returning function, just keep
 	 * using the same cache.  We do not ask funccache.c to re-validate the
@@ -1597,6 +1614,9 @@ fmgr_sql(PG_FUNCTION_ARGS)
 	 */
 	fcache = init_sql_fcache(fcinfo, lazyEvalOK);
 
+	/* Mark fcache as active */
+	fcache->active = true;
+
 	/* Remember info that we might need later to construct tuplestore */
 	fcache->tscontext = tscontext;
 	fcache->randomAccess = randomAccess;
@@ -1853,6 +1873,9 @@ fmgr_sql(PG_FUNCTION_ARGS)
 	if (es == NULL)
 		fcache->eslist = NULL;
 
+	/* Mark fcache as inactive */
+	fcache->active = false;
+
 	error_context_stack = sqlerrcontext.previous;
 
 	return result;
