diff --git a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C
index dbc4f0c31506fc33e64fa3e4010eafd2f50054ec..c02cd44b2ff407ae444348e81d19565ee5b489a1 100644
--- a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C
+++ b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2017-2021 OpenCFD Ltd.
+    Copyright (C) 2017-2023 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,8 +36,25 @@ License
 #include "PatchTools.H"
 #include "addToRunTimeSelectionTable.H"
 
+// BACKPORT:
+
+const Foam::Enum
+<
+    Foam::functionObjects::fieldValues::surfaceFieldValue::error_handlerTypes
+>
+Foam::functionObjects::fieldValues::surfaceFieldValue::error_handlerNames
+({
+    { error_handlerTypes::DEFAULT, "default" },
+    { error_handlerTypes::IGNORE, "ignore" },
+    { error_handlerTypes::WARN, "warn" },
+    { error_handlerTypes::STRICT, "strict" },
+});
+
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
+// Max number of warnings
+static constexpr const unsigned maxWarnings = 10u;
+
 namespace Foam
 {
 namespace functionObjects
@@ -136,39 +153,13 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setFaceZoneFaces()
         mesh_.faceZones().indices(selectionNames_)
     );
 
-    // Total number of faces selected
+    // Total number of faces that could be selected (before patch filtering)
     label numFaces = 0;
     for (const label zoneId : zoneIds)
     {
         numFaces += mesh_.faceZones()[zoneId].size();
     }
 
-    if (zoneIds.empty())
-    {
-        FatalErrorInFunction
-            << type() << ' ' << name() << ": "
-            << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
-            << "    No matching face zone(s): "
-            << flatOutput(selectionNames_)  << nl
-            << "    Known face zones: "
-            << flatOutput(mesh_.faceZones().names()) << nl
-            << exit(FatalError);
-    }
-
-    // Could also check this
-    #if 0
-    if (!returnReduce(bool(numFaces), orOp<bool>()))
-    {
-        WarningInFunction
-            << type() << ' ' << name() << ": "
-            << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
-            << "    The faceZone specification: "
-            << flatOutput(selectionNames_) << nl
-            << "    resulted in 0 faces" << nl
-            << exit(FatalError);
-    }
-    #endif
-
     faceId_.resize(numFaces);
     facePatchId_.resize(numFaces);
     faceFlip_.resize(numFaces);
@@ -231,7 +222,75 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setFaceZoneFaces()
     faceId_.resize(numFaces);
     facePatchId_.resize(numFaces);
     faceFlip_.resize(numFaces);
-    nFaces_ = returnReduce(faceId_.size(), sumOp<label>());
+    nFaces_ = returnReduce(numFaces, sumOp<label>());
+
+    if (!nFaces_)
+    {
+        // Raise warning or error
+        refPtr<OSstream> os;
+        bool fatal = false;
+
+        ++nWarnings_;  // Always increment (even if ignore etc)
+
+        switch (emptySurfaceError_)
+        {
+            case error_handlerTypes::IGNORE:
+            {
+                break;
+            }
+
+            case error_handlerTypes::WARN:
+            {
+                if (nWarnings_ <= maxWarnings)
+                {
+                    os.ref(WarningInFunction);
+                }
+                break;
+            }
+
+            // STRICT / DEFAULT
+            default:
+            {
+                os.ref(FatalErrorInFunction);
+                fatal = true;
+                break;
+            }
+        }
+
+        if (os)
+        {
+            os.ref()
+                << type() << ' ' << name() << ": "
+                << regionTypeNames_[regionType_]
+                << '(' << regionName_ << "):" << nl;
+
+            if (zoneIds.empty())
+            {
+                os.ref()
+                    << "    No matching face zones: "
+                    << flatOutput(selectionNames_)  << nl
+                    << "    Known face zones: "
+                    << flatOutput(mesh_.faceZones().names()) << nl;
+            }
+            else
+            {
+                os.ref()
+                    << "    The face zones: "
+                    << flatOutput(selectionNames_) << nl
+                    << "    resulted in 0 faces" << nl;
+            }
+
+            if (fatal)
+            {
+                FatalError<< exit(FatalError);
+            }
+            else if (nWarnings_ == maxWarnings)
+            {
+                os.ref()
+                    << "... suppressing further warnings." << nl;
+            }
+        }
+    }
 }
 
 
@@ -307,36 +366,10 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setPatchFaces()
         patchIds = std::move(selected);
     }
 
-    if (patchIds.empty())
-    {
-        FatalErrorInFunction
-            << type() << ' ' << name() << ": "
-            << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
-            << "    No matching patch name(s): "
-            << flatOutput(selectionNames_)  << nl
-            << "    Known patch names:" << nl
-            << mesh_.boundaryMesh().names() << nl
-            << exit(FatalError);
-    }
-
-    // Could also check this
-    #if 0
-    if (!returnReduce(bool(numFaces), orOp<bool>()))
-    {
-        WarningInFunction
-            << type() << ' ' << name() << ": "
-            << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
-            << "    The patch specification: "
-            << flatOutput(selectionNames_) << nl
-            << "    resulted in 0 faces" << nl
-            << exit(FatalError);
-    }
-    #endif
-
     faceId_.resize(numFaces);
     facePatchId_.resize(numFaces);
     faceFlip_.resize(numFaces);
-    nFaces_ = returnReduce(faceId_.size(), sumOp<label>());
+    nFaces_ = returnReduce(numFaces, sumOp<label>());
 
     numFaces = 0;
     for (const label patchi : patchIds)
@@ -350,6 +383,74 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setPatchFaces()
 
         numFaces += len;
     }
+
+    if (!nFaces_)
+    {
+        // Raise warning or error
+        refPtr<OSstream> os;
+        bool fatal = false;
+
+        ++nWarnings_;  // Always increment (even if ignore etc)
+
+        switch (emptySurfaceError_)
+        {
+            case error_handlerTypes::IGNORE:
+            {
+                break;
+            }
+
+            case error_handlerTypes::WARN:
+            {
+                if (nWarnings_ <= maxWarnings)
+                {
+                    os.ref(WarningInFunction);
+                }
+                break;
+            }
+
+            // STRICT / DEFAULT
+            default:
+            {
+                os.ref(FatalErrorInFunction);
+                fatal = true;
+                break;
+            }
+        }
+
+        if (os)
+        {
+            os.ref()
+                << type() << ' ' << name() << ": "
+                << regionTypeNames_[regionType_]
+                << '(' << regionName_ << "):" << nl;
+
+            if (patchIds.empty())
+            {
+                os.ref()
+                    << "    No matching patches: "
+                    << flatOutput(selectionNames_)  << nl
+                    << "    Known patch names:" << nl
+                    << mesh_.boundaryMesh().names() << nl;
+            }
+            else
+            {
+                os.ref()
+                    << "    The patches: "
+                    << flatOutput(selectionNames_) << nl
+                    << "    resulted in 0 faces" << nl;
+            }
+
+            if (fatal)
+            {
+                FatalError<< exit(FatalError);
+            }
+            else if (nWarnings_ == maxWarnings)
+            {
+                os.ref()
+                    << "... suppressing further warnings." << nl;
+            }
+        }
+    }
 }
 
 
@@ -583,20 +684,30 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::update()
         return false;
     }
 
+    // Reset some values
+    totalArea_ = 0;
+    nFaces_ = 0;
+    bool checkEmptyFaces = true;
+
     switch (regionType_)
     {
         case stFaceZone:
         {
+            // Raises warning or error internally, don't check again
             setFaceZoneFaces();
+            checkEmptyFaces = false;
             break;
         }
         case stPatch:
         {
+            // Raises warning or error internally, don't check again
             setPatchFaces();
+            checkEmptyFaces = false;
             break;
         }
         case stObject:
         {
+            // TBD: special handling of cast errors?
             const polySurface& s = dynamicCast<const polySurface>(obr());
             nFaces_ = returnReduce(s.size(), sumOp<label>());
             break;
@@ -610,23 +721,76 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::update()
         // Compiler warning if we forgot an enumeration
     }
 
-    if (nFaces_ == 0)
+    if (nFaces_)
     {
-        FatalErrorInFunction
-            << type() << ' ' << name() << ": "
-            << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
-            << "    Region has no faces" << exit(FatalError);
+        // Appears to be successful
+        needsUpdate_ = false;
+        totalArea_ = totalArea();   // Update the area
+        nWarnings_ = 0u;            // Reset the warnings counter
     }
+    else if (checkEmptyFaces)
+    {
+        // Raise warning or error
+        refPtr<OSstream> os;
+        bool fatal = false;
 
-    totalArea_ = totalArea();
+        ++nWarnings_;  // Always increment (even if ignore etc)
+
+        switch (emptySurfaceError_)
+        {
+            case error_handlerTypes::IGNORE:
+            {
+                break;
+            }
+
+            case error_handlerTypes::WARN:
+            {
+                if (nWarnings_ <= maxWarnings)
+                {
+                    os.ref(WarningInFunction);
+                }
+                break;
+            }
+
+            // STRICT / DEFAULT
+            default:
+            {
+                os.ref(FatalErrorInFunction);
+                fatal = true;
+                break;
+            }
+        }
+
+        if (os)
+        {
+            os.ref()
+                << type() << ' ' << name() << ": "
+                << regionTypeNames_[regionType_]
+                << '(' << regionName_ << "):" << nl
+                << "    Region has no faces" << endl;
+
+            if (fatal)
+            {
+                FatalError<< exit(FatalError);
+            }
+            else if (nWarnings_ == maxWarnings)
+            {
+                os.ref()
+                    << "... suppressing further warnings." << nl;
+            }
+        }
+    }
 
     Log << "    total faces   = " << nFaces_ << nl
         << "    total area    = " << totalArea_ << nl
         << endl;
 
-    writeFileHeader(file());
+    // Emit file header on success or change of state
+    if (nWarnings_ <= 1)
+    {
+        writeFileHeader(file());
+    }
 
-    needsUpdate_ = false;
     return true;
 }
 
@@ -919,10 +1083,12 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::surfaceFieldValue
     ),
     needsUpdate_(true),
     writeArea_(false),
+    emptySurfaceError_(error_handlerTypes::DEFAULT),
     selectionNames_(),
     weightFieldNames_(),
     totalArea_(0),
     nFaces_(0),
+    nWarnings_(0),
     faceId_(),
     facePatchId_(),
     faceFlip_()
@@ -953,10 +1119,12 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::surfaceFieldValue
     ),
     needsUpdate_(true),
     writeArea_(false),
+    emptySurfaceError_(error_handlerTypes::DEFAULT),
     selectionNames_(),
     weightFieldNames_(),
     totalArea_(0),
     nFaces_(0),
+    nWarnings_(0),
     faceId_(),
     facePatchId_(),
     faceFlip_()
@@ -976,10 +1144,19 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::read
 
     needsUpdate_ = true;
     writeArea_ = dict.getOrDefault("writeArea", false);
+    emptySurfaceError_ = error_handlerNames.getOrDefault
+    (
+        "empty-surface",
+        dict,
+        error_handlerTypes::DEFAULT,
+        true  // Failsafe behaviour
+    );
+
     weightFieldNames_.clear();
 
     totalArea_ = 0;
     nFaces_ = 0;
+    nWarnings_ = 0;
     faceId_.clear();
     facePatchId_.clear();
     faceFlip_.clear();
@@ -1159,12 +1336,39 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::write()
         writeCurrentTime(file());
     }
 
+    // Handle ignore/warn about empty-surface
+    if (!nFaces_)
+    {
+        totalArea_ = 0;  // Update the area (safety)
+
+        if (operation_ != opNone)
+        {
+            if (emptySurfaceError_ == error_handlerTypes::WARN)
+            {
+                if (writeArea_)
+                {
+                    Log << "    total area = " << totalArea_ << endl;
+                    file() << tab << totalArea_;
+                }
+
+                file() << tab << "NaN";
+                Log << endl;
+            }
+
+            file() << endl;
+        }
+
+        // Early exit on error
+        return true;
+    }
+
     if (writeArea_)
     {
+        // Update the area
         totalArea_ = totalArea();
         Log << "    total area = " << totalArea_ << endl;
 
-        if (operation_ != opNone && Pstream::master())
+        if (operation_ != opNone && UPstream::master())
         {
             file() << tab << totalArea_;
         }
diff --git a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H
index 1933802913d8b72bae544fd8f5e549351c03c7ae..e5c6ecfaf317388a1032a037cd6480b8cb5419f7 100644
--- a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H
+++ b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2023 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -88,6 +88,7 @@ Usage
         scaleFactor     1.0;
         writeArea       false;
         surfaceFormat   none;
+        empty-surface   warn;  // default | warn | ignore | strict
 
         // Optional (inherited) entries
         ...
@@ -111,6 +112,7 @@ Usage
       writeArea    | Write the surface area             | bool |  no   | false
       surfaceFormat | Output value format               | word <!--
                 --> | conditional on writeFields  | none
+      empty-surface | Error handling for empty surfaces | enum | no | default
     \endtable
 
     The inherited entries are elaborated in:
@@ -177,12 +179,11 @@ Note
         Instead specify the regionType 'functionObjectSurface' and provide
         the name.
     - Using \c sampledSurface:
-        - not available for surface fields
-        - if interpolate=true they use \c interpolationCellPoint
-          otherwise they use cell values
-        - each triangle in \c sampledSurface is logically only in one cell
-          so interpolation will be wrong when triangles are larger than
-          cells.  This can only happen for sampling on a \c triSurfaceMesh
+        - surface fields only supported by some surfaces
+        - default uses sampleScheme \c cell
+        - each face in \c sampledSurface is logically only in one cell
+          so sampling will be wrong when they are larger than cells.
+          This can only happen for sampling on a \c triSurfaceMesh
         - take care when using isoSurfaces - these might have duplicate
           triangles and so integration might be wrong
 
@@ -382,6 +383,21 @@ private:
         scalar totalArea() const;
 
 
+    // BACKPORT:
+
+        //- Handling of errors. The exact handling depends on the local context.
+        enum class error_handlerTypes : char
+        {
+            DEFAULT = 0,  //!< Default behaviour (local meaning)
+            IGNORE,       //!< Ignore on errors/problems
+            WARN,         //!< Warn on errors/problems
+            STRICT        //!< Fatal on errors/problems
+        };
+
+        //- Names of the error handler types
+        static const Enum<error_handlerTypes> error_handlerNames;
+
+
 protected:
 
     // Protected Data
@@ -401,6 +417,9 @@ protected:
         //- Optionally write the area of the surfaceFieldValue
         bool writeArea_;
 
+        //- Handling of empty surfaces (nFaces = 0). Default is Fatal.
+        error_handlerTypes emptySurfaceError_;  // BACKPORT
+
         //- Extended selections
         wordRes selectionNames_;
 
@@ -413,6 +432,9 @@ protected:
         //- Global number of faces
         label nFaces_;
 
+        //- Number of warnings emitted since the last valid update
+        unsigned nWarnings_;
+
 
     // If operating on mesh faces (faceZone, patch)