diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index df3d4a45917395c09f1a309f7a6b26590a1dceef..083c7d39809f9eade8b1a16e7583b4ae809d61e9 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -720,12 +720,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
 
         HashType ht = outputHashAlgo.empty() ? htUnknown : parseHashType(outputHashAlgo);
 
-        Hash h;
-        if (outputHash->empty()) {
-            h = Hash(ht);
-            printError("warning: found empty hash, assuming you wanted '%s'", h.to_string());
-        } else
-            h = Hash(*outputHash, ht);
+        Hash h = newHashAllowEmpty(*outputHash, ht);
 
         auto outPath = state.store->makeFixedOutputPath(ingestionMethod, h, drvName);
         if (!jsonObject) drv.env["out"] = state.store->printStorePath(outPath);
@@ -1131,14 +1126,9 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
             filterFun = attr.value;
         } else if (n == "recursive")
             method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos) };
-        else if (n == "sha256") {
-            auto hashStr = state.forceStringNoCtx(*attr.value, *attr.pos);
-            if (hashStr == "") {
-                expectedHash = Hash(htSHA256);
-                printError("warning: found empty hash, assuming you wanted '%s'", expectedHash.to_string());
-            } else
-                expectedHash = Hash(hashStr, htSHA256);
-        } else
+        else if (n == "sha256")
+            expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
+        else
             throw EvalError(format("unsupported argument '%1%' to 'addPath', at %2%") % attr.name % *attr.pos);
     }
     if (path.empty())
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index 745f65adfcb13dc307b44f559b9550c30df8717a..1464aa8b4c4fe9371e3feb25075e30741ad65995 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -102,14 +102,9 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
             string n(attr.name);
             if (n == "url")
                 url = state.forceStringNoCtx(*attr.value, *attr.pos);
-            else if (n == "sha256") {
-                auto hashStr = state.forceStringNoCtx(*attr.value, *attr.pos);
-                if (hashStr == "") {
-                    expectedHash = Hash(htSHA256);
-                    printError("warning: found empty hash, assuming you wanted '%s'", expectedHash->to_string());
-                } else
-                    expectedHash = Hash(hashStr, htSHA256);
-            } else if (n == "name")
+            else if (n == "sha256")
+                expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
+            else if (n == "name")
                 name = state.forceStringNoCtx(*attr.value, *attr.pos);
             else
                 throw EvalError("unsupported argument '%s' to '%s', at %s",
diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc
index 988a8dc69239aa9dcf7dade30c5c3e7dde29bef1..91b897e62c68cfb825f3058676eace998c67dde8 100644
--- a/src/libfetchers/fetchers.cc
+++ b/src/libfetchers/fetchers.cc
@@ -34,14 +34,9 @@ std::unique_ptr<Input> inputFromAttrs(const Attrs & attrs)
     for (auto & inputScheme : *inputSchemes) {
         auto res = inputScheme->inputFromAttrs(attrs2);
         if (res) {
-            if (auto narHash = maybeGetStrAttr(attrs, "narHash")) {
-                if (narHash->empty()) {
-                    res->narHash = Hash(htUnknown);
-                    printError("warning: found empty hash, assuming you wanted '%s'", res->narHash->to_string());
-                } else
-                    // FIXME: require SRI hash.
-                    res->narHash = Hash(*narHash);
-            }
+            if (auto narHash = maybeGetStrAttr(attrs, "narHash"))
+                // FIXME: require SRI hash.
+                res->narHash = newHashAllowEmpty(*narHash, htUnknown);
             return res;
         }
     }
diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc
index e4dafec0b48c63e0b739bece824be0b224fd3c19..937f86bc68873be73044b9458720186c3b6302a5 100644
--- a/src/libfetchers/tarball.cc
+++ b/src/libfetchers/tarball.cc
@@ -263,14 +263,8 @@ struct TarballInputScheme : InputScheme
                 throw Error("unsupported tarball input attribute '%s'", name);
 
         auto input = std::make_unique<TarballInput>(parseURL(getStrAttr(attrs, "url")));
-        if (auto hash = maybeGetStrAttr(attrs, "hash")) {
-            if (hash->empty()) {
-                input->hash = Hash(htUnknown);
-                printError("warning: found empty hash, assuming you wanted '%s'", input->hash->to_string());
-            } else
-                // FIXME: require SRI hash.
-                input->hash = Hash(*hash);
-        }
+        if (auto hash = maybeGetStrAttr(attrs, "hash"))
+            input->hash = newHashAllowEmpty(*hash, htUnknown);
 
         return input;
     }
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 7caee1da7f8b5a82f2bda6d4572457897fded643..2c8085e42919d746f2b995aeba08e814f2a23260 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -205,6 +205,16 @@ Hash::Hash(const std::string & s, HashType type)
         throw BadHash("hash '%s' has wrong length for hash type '%s'", s, printHashType(type));
 }
 
+Hash newHashAllowEmpty(std::string hashStr, HashType ht)
+{
+    if (hashStr.empty())
+    {
+        Hash h(ht);
+        warn("found empty hash, assuming you wanted '%s'", h.to_string());
+    } else
+        return Hash(hashStr, ht);
+}
+
 
 union Ctx
 {
diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh
index ea9fca3e74f2e0565d7a580928bf210be2609a21..449cb1b865657028006e98a7393c37ee1ef348f0 100644
--- a/src/libutil/hash.hh
+++ b/src/libutil/hash.hh
@@ -94,6 +94,8 @@ struct Hash
     }
 };
 
+/* Helper that defaults empty hashes to the 0 hash. */
+Hash newHashAllowEmpty(std::string hashStr, HashType ht);
 
 /* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
 string printHash16or32(const Hash & hash);