diff --git a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C
index dbc4f0c31506fc33e64fa3e4010eafd2f50054ec..a816af06e72013c7cd2a8ba22a8c7560f87221a1 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.
@@ -38,6 +38,9 @@ License
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
+// Max number of warnings
+static constexpr const unsigned maxWarnings = 10u;
+
 namespace Foam
 {
 namespace functionObjects
@@ -136,39 +139,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 +208,73 @@ 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;
+
+        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 +350,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 +367,72 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::setPatchFaces()
 
         numFaces += len;
     }
+
+    if (!nFaces_)
+    {
+        refPtr<OSstream> os;
+        bool fatal = false;
+
+        // Raise warning or error
+        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 +666,29 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::update()
         return false;
     }
 
+    // Reset some values
+    totalArea_ = 0;
+    nFaces_ = 0;
+
     switch (regionType_)
     {
         case stFaceZone:
         {
+            // Raises warning or error internally
             setFaceZoneFaces();
+            if (!nFaces_) return true;
             break;
         }
         case stPatch:
         {
+            // Raises warning or error internally
             setPatchFaces();
+            if (!nFaces_) return true;
             break;
         }
         case stObject:
         {
+            // TBD: special handling of cast errors?
             const polySurface& s = dynamicCast<const polySurface>(obr());
             nFaces_ = returnReduce(s.size(), sumOp<label>());
             break;
@@ -610,12 +702,61 @@ 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 - reset warnings counter
+        nWarnings_ = 0u;
+    }
+    else
+    {
+        // Raise warning or error
+        refPtr<OSstream> os;
+        bool fatal = false;
+
+        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;
+            }
+        }
+        return true;
     }
 
     totalArea_ = totalArea();
@@ -919,10 +1060,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 +1096,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 +1121,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();
@@ -1154,6 +1308,12 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::write()
 
     update();
 
+    if (!nFaces_)
+    {
+        // Encountered an error
+        return true;
+    }
+
     if (operation_ != opNone)
     {
         writeCurrentTime(file());
diff --git a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H
index 1933802913d8b72bae544fd8f5e549351c03c7ae..1e8ece066817c16adc680627a013d980bb8abe9b 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:
@@ -401,6 +403,9 @@ protected:
         //- Optionally write the area of the surfaceFieldValue
         bool writeArea_;
 
+        //- Handling of empty surfaces (nFaces = 0). Default is Fatal.
+        error::handlerTypes emptySurfaceError_;
+
         //- Extended selections
         wordRes selectionNames_;
 
@@ -413,6 +418,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)