diff --git a/tree/dataframe/inc/ROOT/RDFNodes.hxx b/tree/dataframe/inc/ROOT/RDFNodes.hxx
index 337e6b7cc00e046ab0f289428c8efa267f715018..c2ef9d397bd919cf3379be2da63420d5e6854e5f 100644
--- a/tree/dataframe/inc/ROOT/RDFNodes.hxx
+++ b/tree/dataframe/inc/ROOT/RDFNodes.hxx
@@ -500,6 +500,7 @@ protected:
                                /// graph. It is only guaranteed to contain a valid address during an
                                /// event loop.
    const unsigned int fNSlots; ///< Number of thread slots used by this node.
+   bool fHasRun = false;
 
    RBookedCustomColumns fCustomColumns;
 
@@ -520,7 +521,7 @@ public:
    /// This method is invoked to update a partial result during the event loop, right before passing the result to a
    /// user-defined callback registered via RResultPtr::RegisterCallback
    virtual void *PartialUpdate(unsigned int slot) = 0;
-   virtual bool HasRun() const = 0;
+   virtual bool HasRun() const { return fHasRun; }
 
    virtual std::shared_ptr< ROOT::Internal::RDF::GraphDrawing::GraphNode> GetGraph() = 0;
 };
@@ -556,7 +557,6 @@ class RAction final : public RActionBase {
    const std::shared_ptr<PrevDataFrame> fPrevDataPtr;
    PrevDataFrame &fPrevData;
    std::vector<RDFValueTuple_t<ColumnTypes_t>> fValues;
-   bool fHasRun = false;
 
 public:
    RAction(Helper &&h, const ColumnNames_t &bl, std::shared_ptr<PrevDataFrame> pd, const RBookedCustomColumns &customColumns)
@@ -645,8 +645,6 @@ public:
    /// user-defined callback registered via RResultPtr::RegisterCallback
    void *PartialUpdate(unsigned int slot) final { return PartialUpdateImpl(slot); }
 
-   bool HasRun() const { return fHasRun; }
-
 private:
    // this overload is SFINAE'd out if Helper does not implement `PartialUpdate`
    // the template parameter is required to defer instantiation of the method to SFINAE time