diff --git a/lib/util/string.ts b/lib/util/string.ts
index 2cad78d692bc9c473f9e150f497d8200cd103e74..8cb2fe951fcb5d9a78ce7e5f7c2c9ec861c7dd11 100644
--- a/lib/util/string.ts
+++ b/lib/util/string.ts
@@ -1,3 +1,4 @@
+import is from '@sindresorhus/is';
 import { logger } from '../logger';
 
 // Return true if the match string is found at index in content
@@ -23,3 +24,8 @@ export function replaceAt(
     content.substr(index + oldString.length)
   );
 }
+
+// Return true if the input is non-empty and not whitespace string
+export function nonEmptyStringAndNotWhitespace(input: unknown): boolean {
+  return is.nonEmptyString(input) && !is.emptyStringOrWhitespace(input);
+}
diff --git a/lib/util/template/index.ts b/lib/util/template/index.ts
index 2bf1bc75af2e1f3a0a5afdba4de442e1a6b74a3c..e2903315e972c2257c60ce3ba05f944769b9cbe6 100644
--- a/lib/util/template/index.ts
+++ b/lib/util/template/index.ts
@@ -161,9 +161,9 @@ function getFilteredObject(input: CompileInput): FilteredObject {
   for (const field of allAllowed) {
     const value = obj[field];
     if (is.array(value)) {
-      res[field] = value.map((element) =>
-        getFilteredObject(element as CompileInput)
-      );
+      res[field] = value
+        .filter(is.plainObject)
+        .map((element) => getFilteredObject(element as CompileInput));
     } else if (is.plainObject(value)) {
       res[field] = getFilteredObject(value);
     } else if (!is.undefined(value)) {
diff --git a/lib/workers/pr/index.spec.ts b/lib/workers/pr/index.spec.ts
index 574ccd28888a0b8883a67bb96b343e95244c9356..25bbf2595d2919cb68546ade97833e076b30660e 100644
--- a/lib/workers/pr/index.spec.ts
+++ b/lib/workers/pr/index.spec.ts
@@ -807,6 +807,26 @@ describe('workers/pr/index', () => {
       expect(result).toEqual(['labelA', 'labelB', 'labelC']);
     });
 
+    it('empty labels ignored', () => {
+      const result = prWorker.prepareLabels({
+        labels: ['labelA', ''],
+        addLabels: [' ', 'labelB'],
+      });
+      expect(result).toBeArrayOfSize(2);
+      expect(result).toEqual(['labelA', 'labelB']);
+    });
+
+    it('null labels ignored', () => {
+      const result = prWorker.prepareLabels({
+        labels: ['labelA', null],
+        // an empty space between two commas in an array is categorized as a null value
+        // eslint-disable-next-line no-sparse-arrays
+        addLabels: ['labelB', '', undefined, , ,],
+      });
+      expect(result).toBeArrayOfSize(2);
+      expect(result).toEqual(['labelA', 'labelB']);
+    });
+
     it('template labels', () => {
       const result = prWorker.prepareLabels({
         labels: ['datasource-{{{datasource}}}'],
@@ -815,5 +835,14 @@ describe('workers/pr/index', () => {
       expect(result).toBeArrayOfSize(1);
       expect(result).toEqual(['datasource-npm']);
     });
+
+    it('template labels with empty datasource', () => {
+      const result = prWorker.prepareLabels({
+        labels: ['{{{datasource}}}', ' {{{datasource}}} '],
+        datasource: null,
+      });
+      expect(result).toBeArrayOfSize(0);
+      expect(result).toEqual([]);
+    });
   });
 });
diff --git a/lib/workers/pr/index.ts b/lib/workers/pr/index.ts
index 38a02645abe19fb4cde1a3a67e177ab258dc1127..42c1d3dbd408baf8c6f688ec12ff02c0dffec7e7 100644
--- a/lib/workers/pr/index.ts
+++ b/lib/workers/pr/index.ts
@@ -13,6 +13,7 @@ import { sampleSize } from '../../util';
 import { stripEmojis } from '../../util/emoji';
 import { deleteBranch, getBranchLastCommitTime } from '../../util/git';
 import { regEx } from '../../util/regex';
+import { nonEmptyStringAndNotWhitespace } from '../../util/string';
 import * as template from '../../util/template';
 import { resolveBranchStatus } from '../branch/status-checks';
 import { Limit, incLimitedValue, isLimitReached } from '../global/limits';
@@ -56,9 +57,10 @@ function prepareAssigneesReviewers(
 export function prepareLabels(config: RenovateConfig): string[] {
   const labels = config.labels ?? [];
   const addLabels = config.addLabels ?? [];
-  return [...new Set([...labels, ...addLabels])].map((label) =>
-    template.compile(label, config)
-  );
+  return [...new Set([...labels, ...addLabels])]
+    .filter(nonEmptyStringAndNotWhitespace)
+    .map((label) => template.compile(label, config))
+    .filter(nonEmptyStringAndNotWhitespace);
 }
 
 export async function addAssigneesReviewers(