diff --git a/lib/util/promises.ts b/lib/util/promises.ts
index 736437bc1db835feb1c93eb3e434e1a4292e6ab8..1b614aa036ab310dbe0b017c0a178da965abc15e 100644
--- a/lib/util/promises.ts
+++ b/lib/util/promises.ts
@@ -10,7 +10,7 @@ function isExternalHostError(err: any): err is ExternalHostError {
   return err instanceof ExternalHostError;
 }
 
-function handleMultipleErrors(errors: Error[]): never {
+export function handleMultipleErrors(errors: Error[]): never {
   const hostError = errors.find(isExternalHostError);
   if (hostError) {
     throw hostError;
diff --git a/lib/workers/repository/process/__snapshots__/fetch.spec.ts.snap b/lib/workers/repository/process/__snapshots__/fetch.spec.ts.snap
index 8352aa00baf95ee68ba013d8fb2c23453d0e5f53..115dc22d89c49d053d536bd4e02e2bcee2d5aecb 100644
--- a/lib/workers/repository/process/__snapshots__/fetch.spec.ts.snap
+++ b/lib/workers/repository/process/__snapshots__/fetch.spec.ts.snap
@@ -44,7 +44,6 @@ exports[`workers/repository/process/fetch fetchUpdates() handles ignored, skippe
         },
         {
           "depName": "skipped",
-          "packageName": "skipped",
           "skipReason": "some-reason",
           "updates": [],
         },
diff --git a/lib/workers/repository/process/fetch.spec.ts b/lib/workers/repository/process/fetch.spec.ts
index 172a6cb4566d8337957db672bc046ada7d90d88a..c674ccd2108043b78e41938dfd85f1088b3fa614 100644
--- a/lib/workers/repository/process/fetch.spec.ts
+++ b/lib/workers/repository/process/fetch.spec.ts
@@ -4,6 +4,7 @@ import { getConfig } from '../../../config/defaults';
 import { MavenDatasource } from '../../../modules/datasource/maven';
 import type { PackageFile } from '../../../modules/manager/types';
 import { ExternalHostError } from '../../../types/errors/external-host-error';
+import { Result } from '../../../util/result';
 import { fetchUpdates } from './fetch';
 import * as lookup from './lookup';
 
@@ -156,7 +157,7 @@ describe('workers/repository/process/fetch', () => {
           },
         ],
       };
-      lookupUpdates.mockRejectedValueOnce(new Error('some error'));
+      lookupUpdates.mockResolvedValueOnce(Result.err(new Error('some error')));
 
       await expect(
         fetchUpdates({ ...config, repoIsOnboarded: true }, packageFiles),
@@ -173,7 +174,7 @@ describe('workers/repository/process/fetch', () => {
           },
         ],
       };
-      lookupUpdates.mockRejectedValueOnce(new Error('some error'));
+      lookupUpdates.mockResolvedValueOnce(Result.err(new Error('some error')));
 
       await expect(
         fetchUpdates({ ...config, repoIsOnboarded: true }, packageFiles),
diff --git a/lib/workers/repository/process/fetch.ts b/lib/workers/repository/process/fetch.ts
index 025aea391bd734845ba52ba95f2aea31f5d6ccf7..0d9094fcef6b89a0aaeb8e9a7499c81fcbb0002b 100644
--- a/lib/workers/repository/process/fetch.ts
+++ b/lib/workers/repository/process/fetch.ts
@@ -1,7 +1,7 @@
 // TODO #22198
 import is from '@sindresorhus/is';
 import { getManagerConfig, mergeChildConfig } from '../../../config';
-import type { RenovateConfig } from '../../../config/types';
+import type { ManagerConfig, RenovateConfig } from '../../../config/types';
 import { logger } from '../../../logger';
 import { getDefaultConfig } from '../../../modules/datasource';
 import { getDefaultVersioning } from '../../../modules/datasource/common';
@@ -17,27 +17,44 @@ import { Result } from '../../../util/result';
 import { LookupStats } from '../../../util/stats';
 import { PackageFiles } from '../package-files';
 import { lookupUpdates } from './lookup';
-import type { LookupUpdateConfig } from './lookup/types';
+import type { LookupUpdateConfig, UpdateResult } from './lookup/types';
 
-async function fetchDepUpdates(
-  packageFileConfig: RenovateConfig & PackageFile,
+type LookupResult = Result<PackageDependency, Error>;
+
+interface LookupTaskResult {
+  packageFileName: string;
+  manager: string;
+  result: LookupResult;
+}
+
+type LookupTask = Promise<LookupTaskResult>;
+
+async function lookup(
+  packageFileConfig: ManagerConfig & PackageFile,
   indep: PackageDependency,
-): Promise<Result<PackageDependency, Error>> {
+): Promise<LookupResult> {
   const dep = clone(indep);
   dep.updates = [];
+
+  if (dep.skipReason) {
+    return Result.ok(dep);
+  }
+
   if (is.string(dep.depName)) {
     dep.depName = dep.depName.trim();
   }
+
   dep.packageName ??= dep.depName;
   if (!is.nonEmptyString(dep.packageName)) {
     dep.skipReason = 'invalid-name';
+    return Result.ok(dep);
   }
+
   if (dep.isInternal && !packageFileConfig.updateInternalDeps) {
     dep.skipReason = 'internal-package';
-  }
-  if (dep.skipReason) {
     return Result.ok(dep);
   }
+
   const { depName } = dep;
   // TODO: fix types
   let depConfig = mergeChildConfig(packageFileConfig, dep);
@@ -46,24 +63,33 @@ async function fetchDepUpdates(
   depConfig.versioning ??= getDefaultVersioning(depConfig.datasource);
   depConfig = applyPackageRules(depConfig, 'pre-lookup');
   depConfig.packageName ??= depConfig.depName;
+
   if (depConfig.ignoreDeps!.includes(depName!)) {
     // TODO: fix types (#22198)
     logger.debug(`Dependency: ${depName!}, is ignored`);
     dep.skipReason = 'ignored';
-  } else if (depConfig.enabled === false) {
+    return Result.ok(dep);
+  }
+
+  if (depConfig.enabled === false) {
     logger.debug(`Dependency: ${depName!}, is disabled`);
     dep.skipReason = 'disabled';
-  } else {
-    if (depConfig.datasource) {
-      const { val: updateResult, err } = await LookupStats.wrap(
-        depConfig.datasource,
-        () =>
-          Result.wrap(lookupUpdates(depConfig as LookupUpdateConfig)).unwrap(),
-      );
-
-      if (updateResult) {
-        Object.assign(dep, updateResult);
-      } else {
+    return Result.ok(dep);
+  }
+
+  if (!depConfig.datasource) {
+    return Result.ok(dep);
+  }
+
+  return LookupStats.wrap(depConfig.datasource, async () => {
+    return await Result.wrap(lookupUpdates(depConfig as LookupUpdateConfig))
+      .onValue((dep) => {
+        logger.trace({ dep }, 'Dependency lookup success');
+      })
+      .onError((err) => {
+        logger.trace({ err, depName }, 'Dependency lookup error');
+      })
+      .catch((err): Result<UpdateResult, Error> => {
         if (
           packageFileConfig.repoIsOnboarded === true ||
           !(err instanceof ExternalHostError)
@@ -72,76 +98,101 @@ async function fetchDepUpdates(
         }
 
         const cause = err.err;
-        dep.warnings ??= [];
-        dep.warnings.push({
-          topic: 'Lookup Error',
-          // TODO: types (#22198)
-          message: `${depName!}: ${cause.message}`,
+        return Result.ok({
+          updates: [],
+          warnings: [
+            {
+              topic: 'Lookup Error',
+              message: `${depName}: ${cause.message}`,
+            },
+          ],
         });
-      }
-    }
-    dep.updates ??= [];
-  }
-  return Result.ok(dep);
+      })
+      .transform((upd): PackageDependency => Object.assign(dep, upd));
+  });
 }
 
-async function fetchManagerPackagerFileUpdates(
+function createLookupTasks(
   config: RenovateConfig,
-  managerConfig: RenovateConfig,
-  pFile: PackageFile,
-): Promise<void> {
-  const { packageFile } = pFile;
-  const packageFileConfig = mergeChildConfig(managerConfig, pFile);
-  if (pFile.extractedConstraints) {
-    packageFileConfig.constraints = {
-      ...pFile.extractedConstraints,
-      ...config.constraints,
-    };
-  }
-  const { manager } = packageFileConfig;
-  const queue = pFile.deps.map(
-    (dep) => async (): Promise<PackageDependency> => {
-      const updates = await fetchDepUpdates(packageFileConfig, dep);
-      return updates.unwrapOrThrow();
-    },
-  );
-  logger.trace(
-    { manager, packageFile, queueLength: queue.length },
-    'fetchManagerPackagerFileUpdates starting with concurrency',
-  );
+  managerPackageFiles: Record<string, PackageFile[]>,
+): LookupTask[] {
+  const lookupTasks: LookupTask[] = [];
 
-  pFile.deps = await p.all(queue);
-  logger.trace({ packageFile }, 'fetchManagerPackagerFileUpdates finished');
-}
+  for (const [manager, packageFiles] of Object.entries(managerPackageFiles)) {
+    const managerConfig = getManagerConfig(config, manager);
 
-async function fetchManagerUpdates(
-  config: RenovateConfig,
-  packageFiles: Record<string, PackageFile[]>,
-  manager: string,
-): Promise<void> {
-  const managerConfig = getManagerConfig(config, manager);
-  const queue = packageFiles[manager].map(
-    (pFile) => (): Promise<void> =>
-      fetchManagerPackagerFileUpdates(config, managerConfig, pFile),
-  );
-  logger.trace(
-    { manager, queueLength: queue.length },
-    'fetchManagerUpdates starting',
-  );
-  await p.all(queue);
-  logger.trace({ manager }, 'fetchManagerUpdates finished');
+    for (const packageFile of packageFiles) {
+      const packageFileConfig = mergeChildConfig(managerConfig, packageFile);
+      if (packageFile.extractedConstraints) {
+        packageFileConfig.constraints = {
+          ...packageFile.extractedConstraints,
+          ...config.constraints,
+        };
+      }
+
+      for (const dep of packageFile.deps) {
+        lookupTasks.push(
+          lookup(packageFileConfig, dep).then((result) => ({
+            packageFileName: packageFile.packageFile,
+            manager: managerConfig.manager,
+            result,
+          })),
+        );
+      }
+    }
+  }
+
+  return lookupTasks;
 }
 
 export async function fetchUpdates(
   config: RenovateConfig,
-  packageFiles: Record<string, PackageFile[]>,
+  managerPackageFiles: Record<string, PackageFile[]>,
 ): Promise<void> {
-  const managers = Object.keys(packageFiles);
-  const allManagerJobs = managers.map((manager) =>
-    fetchManagerUpdates(config, packageFiles, manager),
+  logger.debug(
+    { baseBranch: config.baseBranch },
+    'Starting package releases lookups',
   );
-  await Promise.all(allManagerJobs);
-  PackageFiles.add(config.baseBranch!, { ...packageFiles });
+
+  const allTasks = createLookupTasks(config, managerPackageFiles);
+
+  const fetchResults = await Promise.all(allTasks);
+
+  const errors: Error[] = [];
+
+  type Manager = string;
+  type PackageFileName = string;
+  type PackageFileDeps = Record<PackageFileName, PackageDependency[]>;
+  type ManagerPackageFileDeps = Record<Manager, PackageFileDeps>;
+  const deps: ManagerPackageFileDeps = {};
+
+  // Separate good results from errors
+  for (const { packageFileName, manager, result } of fetchResults) {
+    const { val: dep, err } = result.unwrap();
+    if (dep) {
+      deps[manager] ??= {};
+      deps[manager][packageFileName] ??= [];
+      deps[manager][packageFileName].push(dep);
+    } else {
+      errors.push(err);
+    }
+  }
+
+  if (errors.length) {
+    p.handleMultipleErrors(errors);
+  }
+
+  // Assign fetched deps back to packageFiles
+  for (const [manager, packageFiles] of Object.entries(managerPackageFiles)) {
+    for (const packageFile of packageFiles) {
+      const packageFileDeps = deps[manager]?.[packageFile.packageFile];
+      if (packageFileDeps) {
+        packageFile.deps = packageFileDeps;
+      }
+    }
+  }
+
+  PackageFiles.add(config.baseBranch!, { ...managerPackageFiles });
   logger.debug(
     { baseBranch: config.baseBranch },
     'Package releases lookups complete',