diff --git a/lib/modules/manager/npm/extract/locked-versions.spec.ts b/lib/modules/manager/npm/extract/locked-versions.spec.ts
index 119eb2faba428bded6a674138e54a9e72c40f2df..c63a8494d8b4283a558d54dc03cf1753b081ef86 100644
--- a/lib/modules/manager/npm/extract/locked-versions.spec.ts
+++ b/lib/modules/manager/npm/extract/locked-versions.spec.ts
@@ -1,14 +1,20 @@
-import { logger } from '../../../../../test/util';
+import { logger, mocked } from '../../../../../test/util';
 import type { PackageFile } from '../../types';
 import type { NpmManagerData } from '../types';
 import { getLockedVersions } from './locked-versions';
+import * as _npm from './npm';
+import * as _pnpm from './pnpm';
+import * as _yarn from './yarn';
 
-const npm = require('./npm');
-const pnpm = require('./pnpm');
-const yarn = require('./yarn');
+const npm = mocked(_npm);
+const pnpm = mocked(_pnpm);
+const yarn = mocked(_yarn);
 
 jest.mock('./npm');
-jest.mock('./yarn');
+jest.mock('./yarn', () => ({
+  ...jest.requireActual<any>('./yarn'),
+  getYarnLock: jest.fn(),
+}));
 jest.mock('./pnpm');
 
 describe('modules/manager/npm/extract/locked-versions', () => {
@@ -52,7 +58,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
       const yarnVersion = '1.22.0';
       const lockfileVersion = undefined;
       const isYarn1 = true;
-      yarn.getYarnLock.mockReturnValue({
+      yarn.getYarnLock.mockResolvedValue({
         isYarn1,
         lockfileVersion,
         lockedVersions,
@@ -89,7 +95,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
       const yarnVersion = '2.1.0';
       const lockfileVersion = undefined;
       const isYarn1 = false;
-      yarn.getYarnLock.mockReturnValue({
+      yarn.getYarnLock.mockResolvedValue({
         isYarn1,
         lockfileVersion,
         lockedVersions,
@@ -136,7 +142,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
       const yarnVersion = '2.2.0';
       const lockfileVersion = 6;
       const isYarn1 = false;
-      yarn.getYarnLock.mockReturnValue({
+      yarn.getYarnLock.mockResolvedValue({
         isYarn1,
         lockfileVersion,
         lockedVersions,
@@ -183,7 +189,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
       const yarnVersion = '3.0.0';
       const lockfileVersion = 8;
       const isYarn1 = false;
-      yarn.getYarnLock.mockReturnValue({
+      yarn.getYarnLock.mockResolvedValue({
         isYarn1,
         lockfileVersion,
         lockedVersions,
@@ -222,7 +228,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
       const yarnVersion = '3.2.0';
       const lockfileVersion = 8;
       const isYarn1 = false;
-      yarn.getYarnLock.mockReturnValue({
+      yarn.getYarnLock.mockResolvedValue({
         isYarn1,
         lockfileVersion,
         lockedVersions,
@@ -259,7 +265,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
     });
 
     it('uses package-lock.json with npm v6.0.0', async () => {
-      npm.getNpmLock.mockReturnValue({
+      npm.getNpmLock.mockResolvedValue({
         lockedVersions: { a: '1.0.0', b: '2.0.0', c: '3.0.0' },
         lockfileVersion: 1,
       });
@@ -290,7 +296,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
     });
 
     it('uses package-lock.json with npm v7.0.0', async () => {
-      npm.getNpmLock.mockReturnValue({
+      npm.getNpmLock.mockResolvedValue({
         lockedVersions: { a: '1.0.0', b: '2.0.0', c: '3.0.0' },
         lockfileVersion: 2,
       });
@@ -327,7 +333,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
     });
 
     it('augments v2 lock file constraint', async () => {
-      npm.getNpmLock.mockReturnValue({
+      npm.getNpmLock.mockResolvedValue({
         lockedVersions: { a: '1.0.0', b: '2.0.0', c: '3.0.0' },
         lockfileVersion: 2,
       });
@@ -364,7 +370,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
     });
 
     it('skips augmenting v2 lock file constraint', async () => {
-      npm.getNpmLock.mockReturnValue({
+      npm.getNpmLock.mockResolvedValue({
         lockedVersions: { a: '1.0.0', b: '2.0.0', c: '3.0.0' },
         lockfileVersion: 2,
       });
@@ -401,7 +407,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
     });
 
     it('appends <7 to npm extractedConstraints', async () => {
-      npm.getNpmLock.mockReturnValue({
+      npm.getNpmLock.mockResolvedValue({
         lockedVersions: {
           a: '1.0.0',
           b: '2.0.0',
@@ -446,7 +452,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
     });
 
     it('skips appending <7 to npm extractedConstraints', async () => {
-      npm.getNpmLock.mockReturnValue({
+      npm.getNpmLock.mockResolvedValue({
         lockedVersions: {
           a: '1.0.0',
           b: '2.0.0',
@@ -492,7 +498,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
   });
 
   it('uses pnpm-lock', async () => {
-    pnpm.getPnpmLock.mockReturnValue({
+    pnpm.getPnpmLock.mockResolvedValue({
       lockedVersionsWithPath: {
         '.': {
           dependencies: {
@@ -553,7 +559,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
   });
 
   it('uses pnpm-lock in subfolder', async () => {
-    pnpm.getPnpmLock.mockReturnValue({
+    pnpm.getPnpmLock.mockResolvedValue({
       lockedVersionsWithPath: {
         '.': {
           dependencies: {
@@ -614,7 +620,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
   });
 
   it('uses pnpm-lock with workspaces', async () => {
-    pnpm.getPnpmLock.mockReturnValue({
+    pnpm.getPnpmLock.mockResolvedValue({
       lockedVersionsWithPath: {
         'workspace-package': {
           dependencies: {
@@ -692,7 +698,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
   });
 
   it('should log warning if unsupported lockfileVersion is found', async () => {
-    npm.getNpmLock.mockReturnValue({
+    npm.getNpmLock.mockResolvedValue({
       lockedVersions: {},
       lockfileVersion: 99,
     });
@@ -722,7 +728,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
 
   describe('lockfileVersion 3', () => {
     it('uses package-lock.json with npm v9.0.0', async () => {
-      npm.getNpmLock.mockReturnValue({
+      npm.getNpmLock.mockResolvedValue({
         lockedVersions: {
           a: '1.0.0',
           b: '2.0.0',
@@ -763,7 +769,7 @@ describe('modules/manager/npm/extract/locked-versions', () => {
     });
 
     it('uses package-lock.json with npm v7.0.0', async () => {
-      npm.getNpmLock.mockReturnValue({
+      npm.getNpmLock.mockResolvedValue({
         lockedVersions: {
           a: '1.0.0',
           b: '2.0.0',
diff --git a/lib/modules/manager/npm/extract/locked-versions.ts b/lib/modules/manager/npm/extract/locked-versions.ts
index 6195f9589739896923046b571fe006dd6d56f410..7e3f1e4a80bd9debd5ae9fe938b4da6ed487374b 100644
--- a/lib/modules/manager/npm/extract/locked-versions.ts
+++ b/lib/modules/manager/npm/extract/locked-versions.ts
@@ -7,7 +7,7 @@ import type { NpmManagerData } from '../types';
 import { getNpmLock } from './npm';
 import { getPnpmLock } from './pnpm';
 import type { LockFile } from './types';
-import { getYarnLock } from './yarn';
+import { getYarnLock, getYarnVersionFromLock } from './yarn';
 
 export async function getLockedVersions(
   packageFiles: PackageFile<NpmManagerData>[]
@@ -25,18 +25,10 @@ export async function getLockedVersions(
         logger.trace(`Retrieving/parsing ${yarnLock}`);
         lockFileCache[yarnLock] = await getYarnLock(yarnLock);
       }
-      const { lockfileVersion, isYarn1 } = lockFileCache[yarnLock];
+      const { isYarn1 } = lockFileCache[yarnLock];
       let yarn: string | undefined;
       if (!isYarn1 && !packageFile.extractedConstraints?.yarn) {
-        if (lockfileVersion && lockfileVersion >= 8) {
-          // https://github.com/yarnpkg/berry/commit/9bcd27ae34aee77a567dd104947407532fa179b3
-          yarn = '^3.0.0';
-        } else if (lockfileVersion && lockfileVersion >= 6) {
-          // https://github.com/yarnpkg/berry/commit/f753790380cbda5b55d028ea84b199445129f9ba
-          yarn = '^2.2.0';
-        } else {
-          yarn = '^2.0.0';
-        }
+        yarn = getYarnVersionFromLock(lockFileCache[yarnLock]);
       }
       if (yarn) {
         packageFile.extractedConstraints ??= {};
diff --git a/lib/modules/manager/npm/extract/yarn.spec.ts b/lib/modules/manager/npm/extract/yarn.spec.ts
index 594dddce55ee9bab9c5094a0f4f21f2d72b2cbb3..12e572ddc1e47df44f74d9f7d7972fba8cdeaf63 100644
--- a/lib/modules/manager/npm/extract/yarn.spec.ts
+++ b/lib/modules/manager/npm/extract/yarn.spec.ts
@@ -1,6 +1,6 @@
 import { Fixtures } from '../../../../../test/fixtures';
 import { fs } from '../../../../../test/util';
-import { getYarnLock } from './yarn';
+import { getYarnLock, getYarnVersionFromLock } from './yarn';
 
 jest.mock('../../../../util/fs');
 
@@ -55,4 +55,17 @@ describe('modules/manager/npm/extract/yarn', () => {
       expect(Object.keys(res.lockedVersions!)).toHaveLength(14);
     });
   });
+
+  it('getYarnVersionFromLock', () => {
+    expect(getYarnVersionFromLock({ isYarn1: true })).toBe('^1.22.18');
+    expect(getYarnVersionFromLock({ isYarn1: false, lockfileVersion: 8 })).toBe(
+      '^3.0.0'
+    );
+    expect(getYarnVersionFromLock({ isYarn1: false, lockfileVersion: 6 })).toBe(
+      '^2.2.0'
+    );
+    expect(getYarnVersionFromLock({ isYarn1: false, lockfileVersion: 3 })).toBe(
+      '^2.0.0'
+    );
+  });
 });
diff --git a/lib/modules/manager/npm/extract/yarn.ts b/lib/modules/manager/npm/extract/yarn.ts
index 4a0bf63484224dfa4150e236bc97a03b0c48ebf0..d61b53e51b29393d2672966353756a0beaeb04d9 100644
--- a/lib/modules/manager/npm/extract/yarn.ts
+++ b/lib/modules/manager/npm/extract/yarn.ts
@@ -85,3 +85,19 @@ export async function isZeroInstall(yarnrcYmlPath: string): Promise<boolean> {
   }
   return false;
 }
+
+export function getYarnVersionFromLock(lockfile: LockFile): string {
+  const { lockfileVersion, isYarn1 } = lockfile;
+  if (isYarn1) {
+    return '^1.22.18';
+  }
+  if (lockfileVersion && lockfileVersion >= 8) {
+    // https://github.com/yarnpkg/berry/commit/9bcd27ae34aee77a567dd104947407532fa179b3
+    return '^3.0.0';
+  } else if (lockfileVersion && lockfileVersion >= 6) {
+    // https://github.com/yarnpkg/berry/commit/f753790380cbda5b55d028ea84b199445129f9ba
+    return '^2.2.0';
+  }
+
+  return '^2.0.0';
+}
diff --git a/lib/modules/manager/npm/post-update/yarn.spec.ts b/lib/modules/manager/npm/post-update/yarn.spec.ts
index b17e525e865d32f55b9d0ad426f2e2525ab784ed..01f25c822a0e37431a88123719ec235caa9ea68b 100644
--- a/lib/modules/manager/npm/post-update/yarn.spec.ts
+++ b/lib/modules/manager/npm/post-update/yarn.spec.ts
@@ -350,7 +350,7 @@ describe('modules/manager/npm/post-update/yarn', () => {
     Fixtures.mock({});
     const execSnapshots = mockExecAll(new Error('some-error'));
     const res = await yarnHelper.generateLockFile('some-dir', {});
-    expect(fs.readFile).toHaveBeenCalledTimes(2);
+    expect(fs.readFile).toHaveBeenCalledTimes(3);
     expect(res.error).toBeTrue();
     expect(res.lockFile).toBeUndefined();
     expect(fixSnapshots(execSnapshots)).toMatchSnapshot();
diff --git a/lib/modules/manager/npm/post-update/yarn.ts b/lib/modules/manager/npm/post-update/yarn.ts
index 99a1aa110ba82937fd932032fb1ae9c2b9112905..55e1a01bd88ade4a9f7d0005c08d8322b3be76f4 100644
--- a/lib/modules/manager/npm/post-update/yarn.ts
+++ b/lib/modules/manager/npm/post-update/yarn.ts
@@ -24,6 +24,7 @@ import { newlineRegex, regEx } from '../../../../util/regex';
 import { uniqueStrings } from '../../../../util/string';
 import { NpmDatasource } from '../../../datasource/npm';
 import type { PostUpdateConfig, Upgrade } from '../../types';
+import { getYarnLock, getYarnVersionFromLock } from '../extract/yarn';
 import type { NpmManagerData } from '../types';
 import { getNodeToolConstraint } from './node-version';
 import type { GenerateLockFileResult } from './types';
@@ -104,13 +105,13 @@ export async function generateLockFile(
       await getNodeToolConstraint(config, upgrades, lockFileDir, lazyPgkJson),
     ];
     const yarnUpdate = upgrades.find(isYarnUpdate);
-    const yarnCompatibility = yarnUpdate
-      ? yarnUpdate.newValue
-      : config.constraints?.yarn ??
-        getPackageManagerVersion('yarn', await lazyPgkJson.getValue());
+    const yarnCompatibility =
+      (yarnUpdate ? yarnUpdate.newValue : config.constraints?.yarn) ??
+      getPackageManagerVersion('yarn', await lazyPgkJson.getValue()) ??
+      getYarnVersionFromLock(await getYarnLock(lockFileName));
     const minYarnVersion =
       semver.validRange(yarnCompatibility) &&
-      semver.minVersion(yarnCompatibility!);
+      semver.minVersion(yarnCompatibility);
     const isYarn1 = !minYarnVersion || minYarnVersion.major === 1;
     const isYarnDedupeAvailable =
       minYarnVersion && semver.gte(minYarnVersion, '2.2.0');