diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index cd1b11e43d6b5983ba0ac5835d18fdcd3a74aac3..4a586350af19c652984004bbee9c67d9c94dce55 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -404,7 +404,7 @@ jobs:
       - name: Check coverage threshold
         run: |
           pnpm nyc check-coverage -t ./coverage/nyc \
-            --branches 98.99 \
+            --branches 99.4 \
             --functions 100 \
             --lines 100 \
             --statements 100
diff --git a/lib/modules/datasource/go/index.ts b/lib/modules/datasource/go/index.ts
index 1406bb7d0dae78b887725ce59abff35910faacd5..8468524b904bbbd46d2fe94adc430f8f940d90d6 100644
--- a/lib/modules/datasource/go/index.ts
+++ b/lib/modules/datasource/go/index.ts
@@ -77,16 +77,16 @@ export class GoDatasource extends Datasource {
 
     switch (source.datasource) {
       case GitTagsDatasource.id: {
-        return this.direct.git.getDigest?.(source, tag) ?? null;
+        return this.direct.git.getDigest(source, tag);
       }
       case GithubTagsDatasource.id: {
         return this.direct.github.getDigest(source, tag);
       }
       case BitbucketTagsDatasource.id: {
-        return this.direct.bitbucket.getDigest?.(source, tag) ?? null;
+        return this.direct.bitbucket.getDigest(source, tag);
       }
       case GitlabTagsDatasource.id: {
-        return this.direct.gitlab.getDigest?.(source, tag) ?? null;
+        return this.direct.gitlab.getDigest(source, tag);
       }
       /* istanbul ignore next: can never happen, makes lint happy */
       default: {
diff --git a/lib/modules/datasource/hexpm-bob/index.ts b/lib/modules/datasource/hexpm-bob/index.ts
index fb07083aa1d51f3e5c086c2aa9bdec5a606c132f..f1154cdf517332f2af2ad66cc9e259ea822316d2 100644
--- a/lib/modules/datasource/hexpm-bob/index.ts
+++ b/lib/modules/datasource/hexpm-bob/index.ts
@@ -27,7 +27,7 @@ export class HexpmBobDatasource extends Datasource {
   @cache({
     namespace: `datasource-${datasource}`,
     key: ({ registryUrl, packageName }: GetReleasesConfig) =>
-      `${registryUrl ?? defaultRegistryUrl}:${packageName}`,
+      `${registryUrl}:${packageName}`,
   })
   async getReleases({
     registryUrl,
diff --git a/lib/modules/datasource/nuget/v3.ts b/lib/modules/datasource/nuget/v3.ts
index 72db618429c52c0c3226e112ae65bad49269beb8..d515d3c916e40dc95f64007278ea0fb3ec06a55a 100644
--- a/lib/modules/datasource/nuget/v3.ts
+++ b/lib/modules/datasource/nuget/v3.ts
@@ -146,7 +146,7 @@ export async function getReleases(
     return null;
   }
 
-  // istanbul ignore if: only happens when no stable version exists
+  // istanbul ignore next: only happens when no stable version exists
   if (latestStable === null && catalogPages.length) {
     const last = catalogEntries.pop()!;
     latestStable = removeBuildMeta(last.version);
diff --git a/lib/modules/datasource/packagist/index.ts b/lib/modules/datasource/packagist/index.ts
index 9e2b203be86371d0e72b817f6993f90cffcf9ce2..0bef353d3379eec0a1481228783f076956f0c86c 100644
--- a/lib/modules/datasource/packagist/index.ts
+++ b/lib/modules/datasource/packagist/index.ts
@@ -69,7 +69,9 @@ export class PackagistDatasource extends Datasource {
     regFile: RegistryFile
   ): string {
     const { key, hash } = regFile;
-    const fileName = hash ? key.replace('%hash%', hash) : key;
+    const fileName = hash
+      ? key.replace('%hash%', hash)
+      : /* istanbul ignore next: hard to test */ key;
     const url = resolveBaseUrl(regUrl, fileName);
     return url;
   }
diff --git a/lib/modules/datasource/pypi/index.ts b/lib/modules/datasource/pypi/index.ts
index ddc76d5b0b1d8c4f6a5c367a79978b4a0ceb6f19..78231497ceac33550a4491e6eb2900be38881a42 100644
--- a/lib/modules/datasource/pypi/index.ts
+++ b/lib/modules/datasource/pypi/index.ts
@@ -1,6 +1,7 @@
 import url from 'node:url';
 import changelogFilenameRegex from 'changelog-filename-regex';
 import { logger } from '../../../logger';
+import { coerceArray } from '../../../util/array';
 import { parse } from '../../../util/html';
 import { regEx } from '../../../util/regex';
 import { ensureTrailingSlash } from '../../../util/url';
@@ -148,7 +149,7 @@ export class PypiDatasource extends Datasource {
     if (dep.releases) {
       const versions = Object.keys(dep.releases);
       dependency.releases = versions.map((version) => {
-        const releases = dep.releases?.[version] ?? [];
+        const releases = coerceArray(dep.releases?.[version]);
         const { upload_time: releaseTimestamp } = releases[0] || {};
         const isDeprecated = releases.some(({ yanked }) => yanked);
         const result: Release = {
@@ -262,7 +263,7 @@ export class PypiDatasource extends Datasource {
     }
     const versions = Object.keys(releases);
     dependency.releases = versions.map((version) => {
-      const versionReleases = releases[version] ?? [];
+      const versionReleases = coerceArray(releases[version]);
       const isDeprecated = versionReleases.some(({ yanked }) => yanked);
       const result: Release = { version };
       if (isDeprecated) {
diff --git a/lib/modules/datasource/repology/index.spec.ts b/lib/modules/datasource/repology/index.spec.ts
index 7e7df992af3e08bd7c1d473ca5e331188534447e..f102a95da374d6ccf026eb072d2172cf55e72436 100644
--- a/lib/modules/datasource/repology/index.spec.ts
+++ b/lib/modules/datasource/repology/index.spec.ts
@@ -2,6 +2,7 @@ import { getPkgReleases } from '..';
 import { Fixtures } from '../../../../test/fixtures';
 import * as httpMock from '../../../../test/http-mock';
 import { EXTERNAL_HOST_ERROR } from '../../../constants/error-messages';
+import * as hostRules from '../../../util/host-rules';
 import { id as versioning } from '../../versioning/loose';
 import type { RepologyPackage } from './types';
 import { RepologyDatasource } from './index';
@@ -56,6 +57,10 @@ const fixtureJdk = Fixtures.get(`openjdk.json`);
 const fixturePython = Fixtures.get(`python.json`);
 
 describe('modules/datasource/repology/index', () => {
+  beforeEach(() => {
+    hostRules.clear();
+  });
+
   describe('getReleases', () => {
     it('returns null for empty result', async () => {
       mockResolverCall('debian_stable', 'nginx', 'binname', {
@@ -202,6 +207,17 @@ describe('modules/datasource/repology/index', () => {
       ).rejects.toThrow(EXTERNAL_HOST_ERROR);
     });
 
+    it('throws on disabled host', async () => {
+      hostRules.add({ matchHost: repologyHost, enabled: false });
+      expect(
+        await getPkgReleases({
+          datasource,
+          versioning,
+          packageName: 'debian_stable/nginx',
+        })
+      ).toBeNull();
+    });
+
     it('returns correct version for binary package', async () => {
       mockResolverCall('debian_stable', 'nginx', 'binname', {
         status: 200,
diff --git a/lib/modules/datasource/repology/index.ts b/lib/modules/datasource/repology/index.ts
index b07e76b21d53943222ed82d60717fadd432b1d29..86e026bff0b1e67af1c1dd6968956c43a1ba78ca 100644
--- a/lib/modules/datasource/repology/index.ts
+++ b/lib/modules/datasource/repology/index.ts
@@ -219,7 +219,6 @@ export class RepologyDatasource extends Datasource {
       return { releases };
     } catch (err) {
       if (err.message === HOST_DISABLED) {
-        // istanbul ignore next
         logger.trace({ packageName, err }, 'Host disabled');
       } else {
         logger.warn(
diff --git a/lib/modules/manager/ansible-galaxy/collections.ts b/lib/modules/manager/ansible-galaxy/collections.ts
index 951618aace5da8c884539fc5fd3b3543c4ff7a2c..f84bf3bd3ae24da272112f26a609ea85e177e01f 100644
--- a/lib/modules/manager/ansible-galaxy/collections.ts
+++ b/lib/modules/manager/ansible-galaxy/collections.ts
@@ -33,7 +33,9 @@ function interpretLine(
       if (value?.startsWith('git@')) {
         localDependency.packageName = value;
       } else {
-        localDependency.registryUrls = value ? [value] : [];
+        localDependency.registryUrls = value
+          ? [value]
+          : /* istanbul ignore next: should have test */ [];
       }
       break;
     }
@@ -83,7 +85,9 @@ function handleGitDep(
 function handleGalaxyDep(dep: AnsibleGalaxyPackageDependency): void {
   dep.datasource = GalaxyCollectionDatasource.id;
   dep.depName = dep.managerData.name;
-  dep.registryUrls = dep.managerData.source ? [dep.managerData.source] : [];
+  dep.registryUrls = dep.managerData.source
+    ? /* istanbul ignore next: should have test */ [dep.managerData.source]
+    : [];
   dep.currentValue = dep.managerData.version;
 }
 
diff --git a/lib/modules/manager/cocoapods/artifacts.ts b/lib/modules/manager/cocoapods/artifacts.ts
index 7e7f84629058c68bd27639fa83cbe26998ba6ea2..fc9dcb70941ff6397119917b477f4459b89d22df 100644
--- a/lib/modules/manager/cocoapods/artifacts.ts
+++ b/lib/modules/manager/cocoapods/artifacts.ts
@@ -2,6 +2,7 @@ import { quote } from 'shlex';
 import upath from 'upath';
 import { TEMPORARY_ERROR } from '../../../constants/error-messages';
 import { logger } from '../../../logger';
+import { coerceArray } from '../../../util/array';
 import { exec } from '../../../util/exec';
 import type { ExecOptions } from '../../../util/exec/types';
 import {
@@ -134,7 +135,7 @@ export async function updateArtifacts({
         });
       }
     }
-    for (const f of status.deleted || []) {
+    for (const f of coerceArray(status.deleted)) {
       res.push({
         file: {
           type: 'deletion',
diff --git a/lib/modules/manager/cocoapods/extract.ts b/lib/modules/manager/cocoapods/extract.ts
index 009e4917da12deaedc733e6b9a73120e10ce8e02..f873584432565e0af1c8369de26d79b0e1a09d25 100644
--- a/lib/modules/manager/cocoapods/extract.ts
+++ b/lib/modules/manager/cocoapods/extract.ts
@@ -1,6 +1,7 @@
 import { logger } from '../../../logger';
 import { getSiblingFileName, localPathExists } from '../../../util/fs';
 import { newlineRegex, regEx } from '../../../util/regex';
+import { coerceString } from '../../../util/string';
 import { GitTagsDatasource } from '../../datasource/git-tags';
 import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { GitlabTagsDatasource } from '../../datasource/gitlab-tags';
@@ -54,7 +55,7 @@ export function gitDep(parsedLine: ParsedLine): PackageDependency | null {
 
   const platformMatch = regEx(
     /[@/](?<platform>github|gitlab)\.com[:/](?<account>[^/]+)\/(?<repo>[^/]+)/
-  ).exec(git ?? '');
+  ).exec(coerceString(git));
 
   if (platformMatch?.groups) {
     const { account, repo, platform } = platformMatch.groups;
diff --git a/lib/modules/manager/gradle/extract/catalog.ts b/lib/modules/manager/gradle/extract/catalog.ts
index ed11cab6dd8150018343ae86f013a08dd0ec86db..a274e8b4228e508801b09b7f9c5e4d0b7262cccc 100644
--- a/lib/modules/manager/gradle/extract/catalog.ts
+++ b/lib/modules/manager/gradle/extract/catalog.ts
@@ -227,7 +227,9 @@ function extractDependency({
     : null;
   if (isArtifactDescriptor(descriptor)) {
     const { group, name } = descriptor;
-    const groupName = is.nullOrUndefined(versionRef) ? group : versionRef; // usage of common variable should have higher priority than other values
+    const groupName = is.nullOrUndefined(versionRef)
+      ? group
+      : /* istanbul ignore next: hard to test */ versionRef; // usage of common variable should have higher priority than other values
     return {
       depName: `${group}:${name}`,
       groupName,
diff --git a/lib/modules/manager/gradle/extract/consistent-versions-plugin.ts b/lib/modules/manager/gradle/extract/consistent-versions-plugin.ts
index 46b971dd027ee1af1bb684447bdb84cb5a108a0b..ea78d1de4358d7a5e2408cecbe62a5d1a638f543 100644
--- a/lib/modules/manager/gradle/extract/consistent-versions-plugin.ts
+++ b/lib/modules/manager/gradle/extract/consistent-versions-plugin.ts
@@ -1,6 +1,7 @@
 import { logger } from '../../../../logger';
 import * as fs from '../../../../util/fs';
 import { newlineRegex, regEx } from '../../../../util/regex';
+import { coerceString } from '../../../../util/string';
 import type { PackageDependency } from '../../types';
 import type { GradleManagerData } from '../types';
 import { isDependencyString, versionLikeSubstring } from '../utils';
@@ -59,9 +60,9 @@ export function parseGcv(
   propsFileName: string,
   fileContents: Record<string, string | null>
 ): PackageDependency<GradleManagerData>[] {
-  const propsFileContent = fileContents[propsFileName] ?? '';
+  const propsFileContent = coerceString(fileContents[propsFileName]);
   const lockFileName = fs.getSiblingFileName(propsFileName, VERSIONS_LOCK);
-  const lockFileContent = fileContents[lockFileName] ?? '';
+  const lockFileContent = coerceString(fileContents[lockFileName]);
   const lockFileMap = parseLockFile(lockFileContent);
   const [propsFileExactMap, propsFileRegexMap] =
     parsePropsFile(propsFileContent);
diff --git a/lib/modules/manager/maven-wrapper/extract.ts b/lib/modules/manager/maven-wrapper/extract.ts
index 12d91fe2440bec900fe35f619dc16ba01a4f09bc..92e834072ef6893f02d278c21a6d902c488e821f 100644
--- a/lib/modules/manager/maven-wrapper/extract.ts
+++ b/lib/modules/manager/maven-wrapper/extract.ts
@@ -1,4 +1,5 @@
 import { logger } from '../../../logger';
+import { coerceArray } from '../../../util/array';
 import { newlineRegex, regEx } from '../../../util/regex';
 import { MavenDatasource } from '../../datasource/maven';
 import { id as versioning } from '../../versioning/maven';
@@ -15,7 +16,7 @@ const WRAPPER_URL_REGEX = regEx(
 );
 
 function extractVersions(fileContent: string): MavenVersionExtract {
-  const lines = fileContent?.split(newlineRegex) ?? [];
+  const lines = coerceArray(fileContent?.split(newlineRegex));
   const maven = extractLineInfo(lines, DISTRIBUTION_URL_REGEX) ?? undefined;
   const wrapper = extractLineInfo(lines, WRAPPER_URL_REGEX) ?? undefined;
   return { maven, wrapper };
diff --git a/lib/modules/manager/npm/update/locked-dependency/package-lock/index.ts b/lib/modules/manager/npm/update/locked-dependency/package-lock/index.ts
index 0bd1e9a04ba28f5cacf4546a481de5003ec33176..3d220be894aa800c0190c3fb5de8cddf2247befc 100644
--- a/lib/modules/manager/npm/update/locked-dependency/package-lock/index.ts
+++ b/lib/modules/manager/npm/update/locked-dependency/package-lock/index.ts
@@ -151,7 +151,9 @@ export async function updateLockedDependency(
         logger.debug(
           `${depName} can be updated to ${newVersion} in-range with matching constraint "${constraint}" in ${
             // TODO: types (#22198)
-            parentDepName ? `${parentDepName}@${parentVersion!}` : packageFile
+            parentDepName
+              ? `${parentDepName}@${parentVersion!}`
+              : /* istanbul ignore next: hard to test */ packageFile
           }`
         );
       } else if (parentDepName && parentVersion) {
@@ -242,7 +244,8 @@ export async function updateLockedDependency(
       newPackageJsonContent =
         parentUpdateResult.files[packageFile] || newPackageJsonContent;
       newLockFileContent =
-        parentUpdateResult.files[lockFile] || newLockFileContent;
+        parentUpdateResult.files[lockFile] ||
+        /* istanbul ignore next: hard to test */ newLockFileContent;
     }
     const files: Record<string, string> = {};
     if (newLockFileContent) {
diff --git a/lib/modules/manager/terraform/lockfile/__snapshots__/index.spec.ts.snap b/lib/modules/manager/terraform/lockfile/__snapshots__/index.spec.ts.snap
index 41ef8b78e198bf7003b4caa65250434a071ae7f2..574bb97beb0d3fc5ae3aa40ed66b5891dca3453e 100644
--- a/lib/modules/manager/terraform/lockfile/__snapshots__/index.spec.ts.snap
+++ b/lib/modules/manager/terraform/lockfile/__snapshots__/index.spec.ts.snap
@@ -94,11 +94,22 @@ provider "registry.terraform.io/hashicorp/azurerm" {
 }
 
 provider "registry.terraform.io/hashicorp/random" {
-  version     = "2.2.2"
+  version     = "2.2.1"
   constraints = "~> 2.2"
   hashes = [
-    "h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=",
-    "h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=",
+    "h1:Zg1Bpi6vr7b0H6no8kVDfEucn5pvNALivdrVKVHarGs=",
+    "zh:072ce92b0138ee65df2e4e2e6e5f6632fa12a7e6453b91399bad89291855d426",
+    "zh:5731987fe61051515f449033e456ee55207caf17ef41096eb82247810585f53b",
+    "zh:6f18b10175708bb5839e1f2082dcc02651b876786cd54ec415a091f3821807c3",
+    "zh:7fa7737661380d18cba3cdc71c4ec6f2fd281b9d61112f6b48d06ca8bbf97771",
+    "zh:8466cb8fbb4de887b23039082a6e3dc85aeabce86dd808e2a7a65e4e1c51dbae",
+    "zh:888c63417701c13bbe785ab11dc690d4803e6a2156318cf188970b7b6400b99e",
+    "zh:a231df55d36fbad1a6705f5d3be4f7459a73ec76117d13f22aa83c10fc610278",
+    "zh:b62d9a4cd64a2d229070260f4abfef476ebbd7c5511b43e9cdccf23ce938f630",
+    "zh:b6bd1a325f909bb93f7c9bef00eb306bef1e406cbdf557901d755a3e7a4a5448",
+    "zh:b9f59afc23cc5567075f76313214baa1e5ce909325229e23c9a4666f7b26e7f7",
+    "zh:d040220c09b8d9d6bd937572bd5b14bc069af2b883185a873460530d8a1de6e6",
+    "zh:f254c1f943eb016ae07ebe91b23f813dc79f2064616c65f98c8f64ce23be90c4",
   ]
 }
 ",
@@ -114,11 +125,6 @@ exports[`modules/manager/terraform/lockfile/index do full lock file maintenance
     "hashicorp/azurerm",
     "2.56.0",
   ],
-  [
-    "https://registry.terraform.io",
-    "hashicorp/random",
-    "2.2.2",
-  ],
 ]
 `;
 
diff --git a/lib/modules/manager/terraform/lockfile/index.spec.ts b/lib/modules/manager/terraform/lockfile/index.spec.ts
index 354b84fd4fdd8204ed494458092125e972ea5075..75e4c93c48a15715deacc9fbaa741801c2e14134 100644
--- a/lib/modules/manager/terraform/lockfile/index.spec.ts
+++ b/lib/modules/manager/terraform/lockfile/index.spec.ts
@@ -439,20 +439,10 @@ describe('modules/manager/terraform/lockfile/index', () => {
           },
         ],
       })
-      .mockResolvedValueOnce({
+      .mockResolvedValueOnce(
         // random
-        releases: [
-          {
-            version: '2.2.1',
-          },
-          {
-            version: '2.2.2',
-          },
-          {
-            version: '3.0.0',
-          },
-        ],
-      });
+        null
+      );
     mockHash.mockResolvedValue([
       'h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=',
       'h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=',
@@ -480,7 +470,7 @@ describe('modules/manager/terraform/lockfile/index', () => {
       })
     );
 
-    expect(mockHash.mock.calls).toBeArrayOfSize(2);
+    expect(mockHash.mock.calls).toBeArrayOfSize(1);
     expect(mockHash.mock.calls).toMatchSnapshot();
   });
 
diff --git a/lib/modules/manager/terraform/lockfile/index.ts b/lib/modules/manager/terraform/lockfile/index.ts
index 36cc975097dde674877b46c7cdaa4518d2efd684..58c72065e27543cbf7e54de237b9be9872aab0df 100644
--- a/lib/modules/manager/terraform/lockfile/index.ts
+++ b/lib/modules/manager/terraform/lockfile/index.ts
@@ -28,7 +28,6 @@ async function updateAllLocks(
         packageName: lock.packageName,
       };
       const { releases } = (await getPkgReleases(updateConfig)) ?? {};
-      // istanbul ignore if: needs test
       if (!releases) {
         return null;
       }
diff --git a/lib/modules/manager/terraform/lockfile/update-locked.ts b/lib/modules/manager/terraform/lockfile/update-locked.ts
index 5d0988dab9e408f453fa1279eae56514ba513f7f..b2bc6eb5f34aa888599837fccdecb2b1713b518d 100644
--- a/lib/modules/manager/terraform/lockfile/update-locked.ts
+++ b/lib/modules/manager/terraform/lockfile/update-locked.ts
@@ -1,4 +1,5 @@
 import { logger } from '../../../../logger';
+import { coerceString } from '../../../../util/string';
 import type { UpdateLockedConfig, UpdateLockedResult } from '../../types';
 import { extractLocks } from './util';
 
@@ -12,8 +13,10 @@ export function updateLockedDependency(
     `terraform.updateLockedDependency: ${depName}@${currentVersion} -> ${newVersion} [${lockFile}]`
   );
   try {
-    const locked = extractLocks(lockFileContent ?? '');
-    const lockedDep = locked?.find((dep) => dep.packageName === depName ?? '');
+    const locked = extractLocks(coerceString(lockFileContent));
+    const lockedDep = locked?.find(
+      (dep) => dep.packageName === coerceString(depName)
+    );
     if (lockedDep?.version === newVersion) {
       return { status: 'already-updated' };
     }
diff --git a/lib/modules/platform/github/massage-markdown-links.ts b/lib/modules/platform/github/massage-markdown-links.ts
index 8fe4a773f6765fa21975b6160227cff84528cf65..44c3b53616c6a6f40d0c4b1bbfebf65e77f15038 100644
--- a/lib/modules/platform/github/massage-markdown-links.ts
+++ b/lib/modules/platform/github/massage-markdown-links.ts
@@ -2,6 +2,7 @@ import type { Content } from 'mdast';
 import remark from 'remark';
 import type { Plugin, Transformer } from 'unified';
 import { logger } from '../../../logger';
+import { coerceNumber } from '../../../util/number';
 import { regEx } from '../../../util/regex';
 
 interface UrlMatch {
@@ -20,8 +21,8 @@ function massageLink(input: string): string {
 
 function collectLinkPosition(input: string, matches: UrlMatch[]): Plugin {
   const transformer = (tree: Content): void => {
-    const startOffset: number = tree.position?.start.offset ?? 0;
-    const endOffset: number = tree.position?.end.offset ?? 0;
+    const startOffset = coerceNumber(tree.position?.start.offset);
+    const endOffset = coerceNumber(tree.position?.end.offset);
 
     if (tree.type === 'link') {
       const substr = input.slice(startOffset, endOffset);
@@ -39,7 +40,7 @@ function collectLinkPosition(input: string, matches: UrlMatch[]): Plugin {
       const urlMatches = [...tree.value.matchAll(globalUrlReg)];
       for (const match of urlMatches) {
         const [url] = match;
-        const start = startOffset + (match.index ?? 0);
+        const start = startOffset + coerceNumber(match.index);
         const end = start + url.length;
         const newUrl = massageLink(url);
         matches.push({ start, end, replaceTo: `[${url}](${newUrl})` });
diff --git a/lib/modules/platform/gitlab/index.spec.ts b/lib/modules/platform/gitlab/index.spec.ts
index 0fca92571e72a2c8b965216e30d4ec3f25b2cf88..047f14dac5ce02ad057f1f951f1a41f9692a7182 100644
--- a/lib/modules/platform/gitlab/index.spec.ts
+++ b/lib/modules/platform/gitlab/index.spec.ts
@@ -846,6 +846,24 @@ describe('modules/platform/gitlab/index', () => {
       );
       expect(res).toBe('green');
     });
+
+    it('returns yellow if unknown status found', async () => {
+      const scope = await initRepo();
+      scope
+        .get(
+          '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
+        )
+        .reply(200, [
+          { name: 'context-1', status: 'pending' },
+          { name: 'some-context', status: 'something' },
+          { name: 'context-3', status: 'failed' },
+        ]);
+      const res = await gitlab.getBranchStatusCheck(
+        'somebranch',
+        'some-context'
+      );
+      expect(res).toBe('yellow');
+    });
   });
 
   describe('setBranchStatus', () => {
diff --git a/lib/modules/platform/gitlab/index.ts b/lib/modules/platform/gitlab/index.ts
index 24cbda8fbc2a6ed67a2e4aedc26a4e8b3ccfd646..d8981e5c72e64515c3c0339313105bdbac21025d 100644
--- a/lib/modules/platform/gitlab/index.ts
+++ b/lib/modules/platform/gitlab/index.ts
@@ -17,6 +17,7 @@ import {
 } from '../../../constants/error-messages';
 import { logger } from '../../../logger';
 import type { BranchStatus } from '../../../types';
+import { coerceArray } from '../../../util/array';
 import * as git from '../../../util/git';
 import * as hostRules from '../../../util/host-rules';
 import { setBaseUrl } from '../../../util/http/gitlab';
@@ -176,9 +177,10 @@ export async function getRepos(config?: AutodiscoverConfig): Promise<string[]> {
     throw err;
   }
 }
-
-function urlEscape(str: string): string {
-  return str ? str.replace(regEx(/\//g), '%2F') : str;
+function urlEscape(str: string): string;
+function urlEscape(str: string | undefined): string | undefined;
+function urlEscape(str: string | undefined): string | undefined {
+  return str?.replace(regEx(/\//g), '%2F');
 }
 
 export async function getRawFile(
@@ -187,7 +189,7 @@ export async function getRawFile(
   branchOrTag?: string
 ): Promise<string | null> {
   const escapedFileName = urlEscape(fileName);
-  const repo = urlEscape(repoName ?? config.repository);
+  const repo = urlEscape(repoName) ?? config.repository;
   const url =
     `projects/${repo}/repository/files/${escapedFileName}?ref=` +
     (branchOrTag ?? `HEAD`);
@@ -243,11 +245,13 @@ function getRepoUrl(
     const { protocol, host, pathname } = parseUrl(defaults.endpoint)!;
     const newPathname = pathname.slice(0, pathname.indexOf('/api'));
     const url = URL.format({
-      protocol: protocol.slice(0, -1) || 'https',
+      protocol:
+        protocol.slice(0, -1) ||
+        /* istanbul ignore next: should never happen */ 'https',
       // TODO: types (#22198)
       auth: `oauth2:${opts.token!}`,
       host,
-      pathname: newPathname + '/' + repository + '.git',
+      pathname: `${newPathname}/${repository}.git`,
     });
     logger.debug(`Using URL based on configured endpoint, url:${url}`);
     return url;
@@ -1097,7 +1101,7 @@ export async function addReviewers(
     return;
   }
 
-  mr.reviewers = mr.reviewers ?? [];
+  mr.reviewers = coerceArray(mr.reviewers);
   const existingReviewers = mr.reviewers.map((r) => r.username);
   const existingReviewerIDs = mr.reviewers.map((r) => r.id);
 
@@ -1144,7 +1148,7 @@ export async function deleteLabel(
   logger.debug(`Deleting label ${label} from #${issueNo}`);
   try {
     const pr = await getPr(issueNo);
-    const labels = (pr.labels ?? [])
+    const labels = coerceArray(pr.labels)
       .filter((l: string) => l !== label)
       .join(',');
     await gitlabApi.putJson(
diff --git a/lib/modules/versioning/cargo/index.ts b/lib/modules/versioning/cargo/index.ts
index 1ab7546ce2cac0c604c1535556c2c8d5f1d88f51..7508c5781be024ba0be0eb1b2d1669b8707e863f 100644
--- a/lib/modules/versioning/cargo/index.ts
+++ b/lib/modules/versioning/cargo/index.ts
@@ -109,7 +109,9 @@ function getNewValue({
     currentVersion,
     newVersion,
   });
-  let newCargo = newSemver ? npm2cargo(newSemver) : null;
+  let newCargo = newSemver
+    ? npm2cargo(newSemver)
+    : /* istanbul ignore next: should never happen */ null;
   // istanbul ignore if
   if (!newCargo) {
     logger.info(
diff --git a/lib/modules/versioning/composer/index.spec.ts b/lib/modules/versioning/composer/index.spec.ts
index ba67d2f3c3a888d65dff3e413f8ec881d6523297..2fbebc88ef45ab450e4027b362a7c7e2189eca15 100644
--- a/lib/modules/versioning/composer/index.spec.ts
+++ b/lib/modules/versioning/composer/index.spec.ts
@@ -1,9 +1,26 @@
 import { api as semver } from '.';
 
 describe('modules/versioning/composer/index', () => {
+  it.each`
+    version    | expected
+    ${'1.2.0'} | ${1}
+    ${''}      | ${null}
+  `('getMajor("$version") === $expected', ({ version, expected }) => {
+    expect(semver.getMajor(version)).toBe(expected);
+  });
+
+  it.each`
+    version    | expected
+    ${'1.2.0'} | ${2}
+    ${''}      | ${null}
+  `('getMinor("$version") === $expected', ({ version, expected }) => {
+    expect(semver.getMinor(version)).toBe(expected);
+  });
+
   it.each`
     version    | expected
     ${'1.2.0'} | ${0}
+    ${''}      | ${null}
   `('getPatch("$version") === $expected', ({ version, expected }) => {
     expect(semver.getPatch(version)).toBe(expected);
   });
@@ -202,6 +219,7 @@ describe('modules/versioning/composer/index', () => {
     ${'^5'}                   | ${'update-lockfile'} | ${'5.1.0'}        | ${'6.0.0'}       | ${'^6'}
     ${'^0.4.0'}               | ${'replace'}         | ${'0.4'}          | ${'0.5'}         | ${'^0.5.0'}
     ${'^0.4.0'}               | ${'replace'}         | ${'0.4'}          | ${'1.0'}         | ${'^1.0.0'}
+    ${'^0.4.0'}               | ${'replace'}         | ${null}           | ${'1.0'}         | ${'1.0'}
   `(
     'getNewValue("$currentValue", "$rangeStrategy", "$currentVersion", "$newVersion") === "$expected"',
     ({ currentValue, rangeStrategy, currentVersion, newVersion, expected }) => {
diff --git a/lib/modules/versioning/distro.spec.ts b/lib/modules/versioning/distro.spec.ts
index 5ad329d7c933bfa2387eaabd724d4bcdbc301c16..c4e4c82d4694ffb325e4e3e5655cdfcd4bee8b79 100644
--- a/lib/modules/versioning/distro.spec.ts
+++ b/lib/modules/versioning/distro.spec.ts
@@ -158,4 +158,9 @@ describe('modules/versioning/distro', () => {
   it('retrieves non-existent release schedule', () => {
     expect(di.getSchedule('20.06')).toBeNull();
   });
+
+  it('works with debian', () => {
+    const di = new DistroInfo('data/debian-distro-info.json');
+    expect(di.isEolLts('trixie')).toBe(true);
+  });
 });
diff --git a/lib/modules/versioning/docker/index.ts b/lib/modules/versioning/docker/index.ts
index 94f5f02cde82912312d43dad1e65638c816f5e6c..a196189d5b4021ca3d1f48c2a0b57aac04d0876f 100644
--- a/lib/modules/versioning/docker/index.ts
+++ b/lib/modules/versioning/docker/index.ts
@@ -1,4 +1,5 @@
 import { regEx } from '../../../util/regex';
+import { coerceString } from '../../../util/string';
 import { GenericVersion, GenericVersioningApi } from '../generic';
 import type { VersioningApi } from '../types';
 
@@ -70,8 +71,8 @@ class DockerVersioningApi extends GenericVersioningApi {
     }
 
     // equals
-    const suffix1 = parsed1.suffix ?? '';
-    const suffix2 = parsed2.suffix ?? '';
+    const suffix1 = coerceString(parsed1.suffix);
+    const suffix2 = coerceString(parsed2.suffix);
     return suffix2.localeCompare(suffix1);
   }
 
diff --git a/lib/modules/versioning/generic.spec.ts b/lib/modules/versioning/generic.spec.ts
index b0903e37dec9e36146d90dfb557a228b03e9986f..d7946528f85fd5cd050c8256f75b3fc2a489713c 100644
--- a/lib/modules/versioning/generic.spec.ts
+++ b/lib/modules/versioning/generic.spec.ts
@@ -1,4 +1,6 @@
+import { partial } from '../../../test/util';
 import { GenericVersion, GenericVersioningApi } from './generic';
+import type { NewValueConfig } from './types';
 
 describe('modules/versioning/generic', () => {
   const optionalFunctions = [
@@ -104,6 +106,8 @@ describe('modules/versioning/generic', () => {
           newVersion: '3.2.1',
         })
       ).toBe('3.2.1');
+
+      expect(api.getNewValue(partial<NewValueConfig>({}))).toBeNull();
     });
 
     it('isCompatible', () => {
diff --git a/lib/modules/versioning/generic.ts b/lib/modules/versioning/generic.ts
index 32df1a1b9040e53c060173eb1b57567901b30adb..1697ad6ab74bee98f418a8c1389455277d92e998 100644
--- a/lib/modules/versioning/generic.ts
+++ b/lib/modules/versioning/generic.ts
@@ -129,9 +129,8 @@ export abstract class GenericVersioningApi<
     return result ?? null;
   }
 
-  getNewValue(newValueConfig: NewValueConfig): string {
-    const { newVersion } = newValueConfig || {};
-    return newVersion;
+  getNewValue({ newVersion }: NewValueConfig): string | null {
+    return newVersion ?? null;
   }
 
   sortVersions(version: string, other: string): number {
diff --git a/lib/modules/versioning/ruby/range.ts b/lib/modules/versioning/ruby/range.ts
index a490d9c94c14dc1fd3c3d3a05603408827036b9b..cb8deaf75717c6c101474b61ab274bdf726541d1 100644
--- a/lib/modules/versioning/ruby/range.ts
+++ b/lib/modules/versioning/ruby/range.ts
@@ -28,7 +28,7 @@ const parse = (range: string): Range => {
 
   const match = regExp.exec(value);
   if (match?.groups) {
-    const { version = '', operator = '', delimiter = ' ' } = match.groups;
+    const { version, operator = '', delimiter } = match.groups;
     return { version, operator, delimiter };
   }
 
diff --git a/lib/modules/versioning/semver-coerced/index.spec.ts b/lib/modules/versioning/semver-coerced/index.spec.ts
index 95d1ddb87f64c00f03c873ac9d1f4933b2188036..ff01ad95fdacffa806e15ea1ea711c28f4d207ca 100644
--- a/lib/modules/versioning/semver-coerced/index.spec.ts
+++ b/lib/modules/versioning/semver-coerced/index.spec.ts
@@ -44,7 +44,7 @@ describe('modules/versioning/semver-coerced/index', () => {
     });
 
     it('invalid version', () => {
-      expect(semverCoerced.getMajor('xxx')).toBeNull();
+      expect(semverCoerced.getMinor('xxx')).toBeNull();
     });
   });
 
diff --git a/lib/modules/versioning/ubuntu/index.ts b/lib/modules/versioning/ubuntu/index.ts
index 304488dbba56ec298f91b771ebaffec3f377b8f6..4e6e96eb07d8be70c2cd9aab8b82ff28ea5aa90a 100644
--- a/lib/modules/versioning/ubuntu/index.ts
+++ b/lib/modules/versioning/ubuntu/index.ts
@@ -1,4 +1,5 @@
 import { regEx } from '../../../util/regex';
+import { coerceString } from '../../../util/string';
 import { DistroInfo } from '../distro';
 import type { NewValueConfig, VersioningApi } from '../types';
 
@@ -44,7 +45,7 @@ function isStable(version: string): boolean {
 
   const match = ver.match(regEx(/^\d+.\d+/));
 
-  if (!di.isReleased(match ? match[0] : ver)) {
+  if (!di.isReleased(coerceString(match?.[0], ver))) {
     return false;
   }
 
@@ -126,12 +127,7 @@ function minSatisfyingVersion(
   return getSatisfyingVersion(versions, range);
 }
 
-function getNewValue({
-  currentValue,
-  rangeStrategy,
-  currentVersion,
-  newVersion,
-}: NewValueConfig): string {
+function getNewValue({ currentValue, newVersion }: NewValueConfig): string {
   if (di.isCodename(currentValue)) {
     return di.getCodenameByVersion(newVersion);
   }
diff --git a/lib/util/number.spec.ts b/lib/util/number.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f215c1aa1c8c7664fa13de20e81f9c529b871e1
--- /dev/null
+++ b/lib/util/number.spec.ts
@@ -0,0 +1,12 @@
+import { coerceNumber } from './number';
+
+describe('util/number', () => {
+  it.each`
+    val          | def          | expected
+    ${1}         | ${2}         | ${1}
+    ${undefined} | ${2}         | ${2}
+    ${undefined} | ${undefined} | ${0}
+  `('coerceNumber($val, $def) = $expected', ({ val, def, expected }) => {
+    expect(coerceNumber(val, def)).toBe(expected);
+  });
+});
diff --git a/lib/util/number.ts b/lib/util/number.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2fb488c9d3eac5ac802d32b3b7037d8f6a36994f
--- /dev/null
+++ b/lib/util/number.ts
@@ -0,0 +1,12 @@
+/**
+ * Coerces a value to a number with optional default value.
+ * @param val the value to coerce
+ * @param def default value
+ * @returns cocerced value
+ */
+export function coerceNumber(
+  val: number | null | undefined,
+  def?: number
+): number {
+  return val ?? def ?? 0;
+}
diff --git a/lib/util/string.spec.ts b/lib/util/string.spec.ts
index 3394a0236492e86c63eaac676694fbeec77e4df4..9df3c52ac209371c740da67975e60760c9f56251 100644
--- a/lib/util/string.spec.ts
+++ b/lib/util/string.spec.ts
@@ -38,5 +38,6 @@ describe('util/string', () => {
     expect(coerceString('')).toBe('');
     expect(coerceString(undefined)).toBe('');
     expect(coerceString(null)).toBe('');
+    expect(coerceString(null, 'foo')).toBe('foo');
   });
 });
diff --git a/lib/util/string.ts b/lib/util/string.ts
index ce6bad089d9fd5ed19653391fc1a17f1d11510b1..f82e8f260eee7e9af94bf256a5c018315122afaa 100644
--- a/lib/util/string.ts
+++ b/lib/util/string.ts
@@ -83,6 +83,14 @@ export function copystr(x: string): string {
   return buf.toString('utf8');
 }
 
-export function coerceString(val: string | null | undefined): string {
-  return val ?? '';
+/**
+ * Coerce a value to a string with optional default value.
+ * @param val value to coerce
+ * @returns the coerced value.
+ */
+export function coerceString(
+  val: string | null | undefined,
+  def?: string
+): string {
+  return val ?? def ?? '';
 }
diff --git a/lib/workers/global/config/parse/file.spec.ts b/lib/workers/global/config/parse/file.spec.ts
index 3b7d22e34057cd82998fd63062e6d9e70693883f..0a786e8f80a079978be06b5c8a8ef8dbd68e84e3 100644
--- a/lib/workers/global/config/parse/file.spec.ts
+++ b/lib/workers/global/config/parse/file.spec.ts
@@ -38,7 +38,7 @@ describe('workers/global/config/parse/file', () => {
       ['.renovaterc', '.renovaterc'],
       ['JSON5 config file', 'config.json5'],
       ['YAML config file', 'config.yaml'],
-    ])('parses %s', async (fileType, filePath) => {
+    ])('parses %s', async (_fileType, filePath) => {
       const configFile = upath.resolve(__dirname, './__fixtures__/', filePath);
       expect(
         await file.getConfig({ RENOVATE_CONFIG_FILE: configFile })
diff --git a/lib/workers/global/config/parse/file.ts b/lib/workers/global/config/parse/file.ts
index eeeaf5671e11a4bccc8a32491ff9701f99286c70..4ccdca5f392bb64515c08beada1431bdfeb56796 100644
--- a/lib/workers/global/config/parse/file.ts
+++ b/lib/workers/global/config/parse/file.ts
@@ -23,7 +23,9 @@ export async function getParsedContent(file: string): Promise<RenovateConfig> {
       return JSON5.parse(await readSystemFile(file, 'utf8'));
     case '.js': {
       const tmpConfig = await import(file);
-      let config = tmpConfig.default ? tmpConfig.default : tmpConfig;
+      let config = tmpConfig.default
+        ? tmpConfig.default
+        : /* istanbul ignore next: hard to test */ tmpConfig;
       // Allow the config to be a function
       if (is.function_(config)) {
         config = config();
@@ -54,7 +56,6 @@ export async function getConfig(env: NodeJS.ProcessEnv): Promise<AllConfig> {
   try {
     config = await getParsedContent(configFile);
   } catch (err) {
-    // istanbul ignore if
     if (err instanceof SyntaxError || err instanceof TypeError) {
       logger.fatal(`Could not parse config file \n ${err.stack!}`);
       process.exit(1);
@@ -69,10 +70,9 @@ export async function getConfig(env: NodeJS.ProcessEnv): Promise<AllConfig> {
     } else if (env.RENOVATE_CONFIG_FILE) {
       logger.fatal('No custom config file found on disk');
       process.exit(1);
-    } else {
-      // istanbul ignore next: we can ignore this
-      logger.debug('No config file found on disk - skipping');
     }
+    // istanbul ignore next: we can ignore this
+    logger.debug('No config file found on disk - skipping');
   }
 
   await deleteNonDefaultConfig(env); // Try deletion only if RENOVATE_CONFIG_FILE is specified
diff --git a/lib/workers/global/config/parse/index.ts b/lib/workers/global/config/parse/index.ts
index 070d52561d3c8fbf21e123f2699d7f40b4063712..beb03cb895da40366ba4cbfd928f6968848120cc 100644
--- a/lib/workers/global/config/parse/index.ts
+++ b/lib/workers/global/config/parse/index.ts
@@ -3,6 +3,7 @@ import type { AllConfig } from '../../../../config/types';
 import { mergeChildConfig } from '../../../../config/utils';
 import { addStream, logger, setContext } from '../../../../logger';
 import { detectAllGlobalConfig } from '../../../../modules/manager';
+import { coerceArray } from '../../../../util/array';
 import { ensureDir, getParentDir, readSystemFile } from '../../../../util/fs';
 import { addSecretForSanitizing } from '../../../../util/sanitize';
 import { ensureTrailingSlash } from '../../../../util/url';
@@ -95,7 +96,7 @@ export async function parseConfigs(
 
   if (config.detectHostRulesFromEnv) {
     const hostRules = hostRulesFromEnv(env);
-    config.hostRules = [...(config.hostRules ?? []), ...hostRules];
+    config.hostRules = [...coerceArray(config.hostRules), ...hostRules];
   }
   // Get global config
   logger.trace({ config }, 'Full config');
diff --git a/lib/workers/repository/config-migration/branch/migrated-data.spec.ts b/lib/workers/repository/config-migration/branch/migrated-data.spec.ts
index 541d7e7cfdaf5d9cf0a8f2ed6f28aa63f853c84c..db3b27b34648668c2c710fdcede2a913ac947a3b 100644
--- a/lib/workers/repository/config-migration/branch/migrated-data.spec.ts
+++ b/lib/workers/repository/config-migration/branch/migrated-data.spec.ts
@@ -6,7 +6,7 @@ import { migrateConfig } from '../../../../config/migration';
 import { logger } from '../../../../logger';
 import { readLocalFile } from '../../../../util/fs';
 import { detectRepoFileConfig } from '../../init/merge';
-import { MigratedDataFactory } from './migrated-data';
+import { MigratedDataFactory, applyPrettierFormatting } from './migrated-data';
 
 jest.mock('../../../../config/migration');
 jest.mock('../../../../util/git');
@@ -190,5 +190,15 @@ describe('workers/repository/config-migration/branch/migrated-data', () => {
         MigratedDataFactory.applyPrettierFormatting(migratedData)
       ).resolves.toEqual(formatted);
     });
+
+    it('formats with default 2 spaces', async () => {
+      mockedFunction(scm.getFileList).mockResolvedValue(['.prettierrc']);
+      await expect(
+        applyPrettierFormatting(migratedData.content, 'json', {
+          amount: 0,
+          indent: '  ',
+        })
+      ).resolves.toEqual(formattedMigratedData.content);
+    });
   });
 });
diff --git a/lib/workers/repository/config-migration/pr/index.ts b/lib/workers/repository/config-migration/pr/index.ts
index 3d11d730af11ce8656fa7689075c16ee70a2bd00..9eb0355783f40ac486bab75ffb938fd0ae643bab 100644
--- a/lib/workers/repository/config-migration/pr/index.ts
+++ b/lib/workers/repository/config-migration/pr/index.ts
@@ -6,6 +6,7 @@ import { platform } from '../../../../modules/platform';
 import { hashBody } from '../../../../modules/platform/pr-body';
 import { scm } from '../../../../modules/platform/scm';
 import { emojify } from '../../../../util/emoji';
+import { coerceString } from '../../../../util/string';
 import * as template from '../../../../util/template';
 import { joinUrlParts } from '../../../../util/url';
 import { getPlatformPrOptions } from '../../update/pr';
@@ -21,7 +22,7 @@ export async function ensureConfigMigrationPr(
 ): Promise<void> {
   logger.debug('ensureConfigMigrationPr()');
   const docsLink = joinUrlParts(
-    config.productLinks?.documentation ?? '',
+    coerceString(config.productLinks?.documentation),
     'configuration-options/#configmigration'
   );
   const branchName = getMigrationBranchName(config);
diff --git a/lib/workers/repository/finalize/repository-statistics.spec.ts b/lib/workers/repository/finalize/repository-statistics.spec.ts
index 6d65e88af89a6f89a0521ac8fd917197970e73a3..66e05794105833bbb0139219eeb8f4c2e189bae2 100644
--- a/lib/workers/repository/finalize/repository-statistics.spec.ts
+++ b/lib/workers/repository/finalize/repository-statistics.spec.ts
@@ -174,7 +174,6 @@ describe('workers/repository/finalize/repository-statistics', () => {
 
       const branches: BranchCache[] = [{ ...branchCache, branchName: 'b1' }];
       const cache = partial<RepoCacheData>({
-        scan: {},
         branches,
       });
       getCacheSpy.mockReturnValueOnce(cache);
diff --git a/lib/workers/repository/update/pr/changelog/release-notes.spec.ts b/lib/workers/repository/update/pr/changelog/release-notes.spec.ts
index e073ea22662b6cb91aed1a80096e14ce3c7818a1..9d288ee72c5af08250f042a9f8301010ea8a42c2 100644
--- a/lib/workers/repository/update/pr/changelog/release-notes.spec.ts
+++ b/lib/workers/repository/update/pr/changelog/release-notes.spec.ts
@@ -144,6 +144,7 @@ describe('workers/repository/update/pr/changelog/release-notes', () => {
         project: partial<ChangeLogProject>({
           type: 'gitlab',
           repository: 'https://gitlab.com/gitlab-org/gitter/webapp/',
+          sourceDirectory: 'lib',
         }),
         versions: [
           partial<ChangeLogRelease>({
@@ -159,6 +160,7 @@ describe('workers/repository/update/pr/changelog/release-notes', () => {
         project: {
           repository: 'https://gitlab.com/gitlab-org/gitter/webapp/',
           type: 'gitlab',
+          sourceDirectory: 'lib',
         },
         versions: [
           {
diff --git a/lib/workers/repository/update/pr/changelog/release-notes.ts b/lib/workers/repository/update/pr/changelog/release-notes.ts
index b2c4117e2d397a4bb0753804579361d6d52b734f..567a7a55d31bb0e137f9211e355ea12ba25ebfb1 100644
--- a/lib/workers/repository/update/pr/changelog/release-notes.ts
+++ b/lib/workers/repository/update/pr/changelog/release-notes.ts
@@ -7,6 +7,7 @@ import * as packageCache from '../../../../../util/cache/package';
 import { detectPlatform } from '../../../../../util/common';
 import { linkify } from '../../../../../util/markdown';
 import { newlineRegex, regEx } from '../../../../../util/regex';
+import { coerceString } from '../../../../../util/string';
 import { validateUrl } from '../../../../../util/url';
 import type { BranchUpgradeConfig } from '../../../../types';
 import * as bitbucket from './bitbucket';
@@ -81,7 +82,7 @@ export function massageBody(
   input: string | undefined | null,
   baseUrl: string
 ): string {
-  let body = input ?? '';
+  let body = coerceString(input);
   // Convert line returns
   body = body.replace(regEx(/\r\n/g), '\n');
   // semantic-release cleanup
diff --git a/lib/workers/repository/update/pr/changelog/releases.ts b/lib/workers/repository/update/pr/changelog/releases.ts
index eb83f63610a808084a16650c4c433e5903bee02e..9f1f3c8bfe4d35b48ec210806b24cf00789ec5fc 100644
--- a/lib/workers/repository/update/pr/changelog/releases.ts
+++ b/lib/workers/repository/update/pr/changelog/releases.ts
@@ -6,6 +6,7 @@ import {
   isGetPkgReleasesConfig,
 } from '../../../../../modules/datasource';
 import { VersioningApi, get } from '../../../../../modules/versioning';
+import { coerceArray } from '../../../../../util/array';
 import type { BranchUpgradeConfig } from '../../../../types';
 
 function matchesMMP(version: VersioningApi, v1: string, v2: string): boolean {
@@ -57,7 +58,7 @@ export async function getInRangeReleases(
           matchesUnstable(version, newVersion, release.version)
       );
     if (version.valueToVersion) {
-      for (const release of releases || []) {
+      for (const release of coerceArray(releases)) {
         release.version = version.valueToVersion(release.version);
       }
     }