diff --git a/lib/modules/manager/nuget/artifacts.spec.ts b/lib/modules/manager/nuget/artifacts.spec.ts
index 118298839ff221853371628d602d044a81e1f045..81ab0977fbd2398ba7ea264eeb666058a664055a 100644
--- a/lib/modules/manager/nuget/artifacts.spec.ts
+++ b/lib/modules/manager/nuget/artifacts.spec.ts
@@ -6,8 +6,8 @@ import type { RepoGlobalConfig } from '../../../config/types';
 import * as docker from '../../../util/exec/docker';
 import * as _hostRules from '../../../util/host-rules';
 import type { UpdateArtifactsConfig } from '../types';
-import * as nuget from './artifacts';
 import * as util from './util';
+import * as nuget from '.';
 
 jest.mock('child_process');
 jest.mock('../../../util/exec/env');
@@ -66,7 +66,7 @@ describe('modules/manager/nuget/artifacts', () => {
     fs.getSiblingFileName.mockReturnValueOnce(
       'path/with space/packages.lock.json'
     );
-    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
+    git.getFile.mockResolvedValueOnce('Current packages.lock.json');
     fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
     expect(
       await nuget.updateArtifacts({
@@ -82,7 +82,7 @@ describe('modules/manager/nuget/artifacts', () => {
   it('updates lock file', async () => {
     const execSnapshots = mockExecAll(exec);
     fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
-    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
+    git.getFile.mockResolvedValueOnce('Current packages.lock.json');
     fs.readLocalFile.mockResolvedValueOnce('New packages.lock.json');
     expect(
       await nuget.updateArtifacts({
@@ -98,7 +98,7 @@ describe('modules/manager/nuget/artifacts', () => {
   it('does not update lock file when non-proj file is changed', async () => {
     const execSnapshots = mockExecAll(exec);
     fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
-    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
+    git.getFile.mockResolvedValueOnce('Current packages.lock.json');
     fs.readLocalFile.mockResolvedValueOnce('New packages.lock.json');
     expect(
       await nuget.updateArtifacts({
@@ -114,7 +114,7 @@ describe('modules/manager/nuget/artifacts', () => {
   it('does not update lock file when no deps changed', async () => {
     const execSnapshots = mockExecAll(exec);
     fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
-    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
+    git.getFile.mockResolvedValueOnce('Current packages.lock.json');
     fs.readLocalFile.mockResolvedValueOnce('New packages.lock.json');
     expect(
       await nuget.updateArtifacts({
@@ -130,7 +130,7 @@ describe('modules/manager/nuget/artifacts', () => {
   it('performs lock file maintenance', async () => {
     const execSnapshots = mockExecAll(exec);
     fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
-    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
+    git.getFile.mockResolvedValueOnce('Current packages.lock.json');
     fs.readLocalFile.mockResolvedValueOnce('New packages.lock.json');
     expect(
       await nuget.updateArtifacts({
@@ -150,7 +150,7 @@ describe('modules/manager/nuget/artifacts', () => {
     GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
     const execSnapshots = mockExecAll(exec);
     fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
-    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
+    git.getFile.mockResolvedValueOnce('Current packages.lock.json');
     fs.readLocalFile.mockResolvedValueOnce('New packages.lock.json');
     expect(
       await nuget.updateArtifacts({
@@ -167,7 +167,7 @@ describe('modules/manager/nuget/artifacts', () => {
     GlobalConfig.set({ ...adminConfig, binarySource: 'global' });
     const execSnapshots = mockExecAll(exec);
     fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
-    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
+    git.getFile.mockResolvedValueOnce('Current packages.lock.json');
     fs.readLocalFile.mockResolvedValueOnce('New packages.lock.json');
     expect(
       await nuget.updateArtifacts({
@@ -182,7 +182,7 @@ describe('modules/manager/nuget/artifacts', () => {
 
   it('catches errors', async () => {
     fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
-    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
+    git.getFile.mockResolvedValueOnce('Current packages.lock.json');
     fs.writeLocalFile.mockImplementationOnce(() => {
       throw new Error('not found');
     });
@@ -206,7 +206,7 @@ describe('modules/manager/nuget/artifacts', () => {
   it('authenticates at registries', async () => {
     const execSnapshots = mockExecAll(exec);
     fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
-    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
+    git.getFile.mockResolvedValueOnce('Current packages.lock.json');
     fs.readLocalFile.mockResolvedValueOnce('New packages.lock.json');
     getConfiguredRegistries.mockResolvedValueOnce([
       {
@@ -224,7 +224,7 @@ describe('modules/manager/nuget/artifacts', () => {
           password: 'some-password',
         };
       }
-      return undefined;
+      return {};
     });
     expect(
       await nuget.updateArtifacts({
@@ -240,7 +240,7 @@ describe('modules/manager/nuget/artifacts', () => {
   it('strips protocol version from feed url', async () => {
     const execSnapshots = mockExecAll(exec);
     fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
-    fs.readLocalFile.mockResolvedValueOnce('Current packages.lock.json');
+    git.getFile.mockResolvedValueOnce('Current packages.lock.json');
     fs.readLocalFile.mockResolvedValueOnce('New packages.lock.json');
     getConfiguredRegistries.mockResolvedValueOnce([
       {
diff --git a/lib/modules/manager/nuget/artifacts.ts b/lib/modules/manager/nuget/artifacts.ts
index bd3a4e733db657ff233d98c0156627102db9a564..985062c6fabe1569f584786f65a28dbab6e312bd 100644
--- a/lib/modules/manager/nuget/artifacts.ts
+++ b/lib/modules/manager/nuget/artifacts.ts
@@ -12,6 +12,7 @@ import {
   remove,
   writeLocalFile,
 } from '../../../util/fs';
+import { getFile } from '../../../util/git';
 import * as hostRules from '../../../util/host-rules';
 import { regEx } from '../../../util/regex';
 import { NugetDatasource } from '../../datasource/nuget';
@@ -30,7 +31,7 @@ import {
 
 async function addSourceCmds(
   packageFileName: string,
-  config: UpdateArtifactsConfig,
+  _config: UpdateArtifactsConfig,
   nugetConfigFile: string
 ): Promise<string[]> {
   const registries =
@@ -93,15 +94,15 @@ async function runDotnetRestore(
 }
 
 async function getLockFileContentMap(
-  lockFileNames: string[]
-): Promise<Record<string, string>> {
-  const lockFileContentMap: Record<string, string> = {};
+  lockFileNames: string[],
+  local = false
+): Promise<Record<string, string | null>> {
+  const lockFileContentMap: Record<string, string | null> = {};
 
   for (const lockFileName of lockFileNames) {
-    lockFileContentMap[lockFileName] = await readLocalFile(
-      lockFileName,
-      'utf8'
-    );
+    lockFileContentMap[lockFileName] = local
+      ? await readLocalFile(lockFileName, 'utf8')
+      : await getFile(lockFileName);
   }
 
   return lockFileContentMap;
@@ -166,7 +167,10 @@ export async function updateArtifacts({
 
     await runDotnetRestore(packageFileName, packageFiles, config);
 
-    const newLockFileContentMap = await getLockFileContentMap(lockFileNames);
+    const newLockFileContentMap = await getLockFileContentMap(
+      lockFileNames,
+      true
+    );
 
     const retArray: UpdateArtifactsResult[] = [];
     for (const lockFileName of lockFileNames) {
@@ -175,15 +179,16 @@ export async function updateArtifacts({
         newLockFileContentMap[lockFileName]
       ) {
         logger.trace(`Lock file ${lockFileName} is unchanged`);
-      } else {
+      } else if (newLockFileContentMap[lockFileName]) {
         retArray.push({
           file: {
             type: 'addition',
             path: lockFileName,
-            contents: newLockFileContentMap[lockFileName],
+            contents: newLockFileContentMap[lockFileName]!,
           },
         });
       }
+      // TODO: else should we return an artifact error if new content is missing?
     }
 
     return retArray.length > 0 ? retArray : null;
diff --git a/lib/modules/manager/nuget/extract.spec.ts b/lib/modules/manager/nuget/extract.spec.ts
index ff321d4518824a761e96b034e2745be5a25b5137..95089eab3c341deb82b06cc1fca2e60cf4f45732 100644
--- a/lib/modules/manager/nuget/extract.spec.ts
+++ b/lib/modules/manager/nuget/extract.spec.ts
@@ -3,7 +3,7 @@ import { loadFixture } from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
 import type { ExtractConfig } from '../types';
-import { extractPackageFile } from './extract';
+import { extractPackageFile } from '.';
 
 const config: ExtractConfig = {};
 
@@ -32,24 +32,24 @@ describe('modules/manager/nuget/extract', () => {
         'with-centralized-package-versions/Directory.Packages.props';
       const sample = loadFixture(packageFile);
       const res = await extractPackageFile(sample, packageFile, config);
-      expect(res.deps).toMatchSnapshot();
-      expect(res.deps).toHaveLength(1);
+      expect(res?.deps).toMatchSnapshot();
+      expect(res?.deps).toHaveLength(1);
     });
 
     it('extracts all dependencies', async () => {
       const packageFile = 'sample.csproj';
       const sample = loadFixture(packageFile);
       const res = await extractPackageFile(sample, packageFile, config);
-      expect(res.deps).toMatchSnapshot();
-      expect(res.deps).toHaveLength(17);
+      expect(res?.deps).toMatchSnapshot();
+      expect(res?.deps).toHaveLength(17);
     });
 
     it('extracts all dependencies from global packages file', async () => {
       const packageFile = 'packages.props';
       const sample = loadFixture(packageFile);
       const res = await extractPackageFile(sample, packageFile, config);
-      expect(res.deps).toMatchSnapshot();
-      expect(res.deps).toHaveLength(17);
+      expect(res?.deps).toMatchSnapshot();
+      expect(res?.deps).toHaveLength(17);
     });
 
     it('considers NuGet.config', async () => {
diff --git a/lib/util/fs/index.ts b/lib/util/fs/index.ts
index ce34696cfbc422aa00a5fecfee2ef21c8b84eb7b..4b224bdc13b026cfbbc5b8b04745a414e6bcd135 100644
--- a/lib/util/fs/index.ts
+++ b/lib/util/fs/index.ts
@@ -23,6 +23,7 @@ export function getSiblingFileName(
   return upath.join(subDirectory, otherFileName);
 }
 
+// TODO: can return null #7154
 export async function readLocalFile(fileName: string): Promise<Buffer>;
 export async function readLocalFile(
   fileName: string,