diff --git a/lib/modules/datasource/maven/s3.spec.ts b/lib/modules/datasource/maven/s3.spec.ts
index 8ec6e5a316463ec374413588db0613fcbfe24836..5030b0d21db7808c0c05e2218553c1b4f52ce1f4 100644
--- a/lib/modules/datasource/maven/s3.spec.ts
+++ b/lib/modules/datasource/maven/s3.spec.ts
@@ -46,7 +46,10 @@ describe('modules/datasource/maven/s3', () => {
           Bucket: 'repobucket',
           Key: 'org/example/package/maven-metadata.xml',
         })
-        .resolvesOnce({ Body: meta as never });
+        .resolvesOnce({
+          Body: meta as never,
+          LastModified: new Date('2020-01-01T00:00Z'),
+        });
 
       const res = await get('org.example:package', baseUrlS3);
 
@@ -89,7 +92,7 @@ describe('modules/datasource/maven/s3', () => {
           {
             failedUrl: 's3://repobucket/org/example/package/maven-metadata.xml',
           },
-          'Dependency lookup authorization failed. Please correct AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY env vars',
+          'Maven S3 lookup error: credentials provider error, check "AWS_ACCESS_KEY_ID" and "AWS_SECRET_ACCESS_KEY" variables',
         );
       });
 
@@ -108,7 +111,7 @@ describe('modules/datasource/maven/s3', () => {
           {
             failedUrl: 's3://repobucket/org/example/package/maven-metadata.xml',
           },
-          'Dependency lookup failed. Please a correct AWS_REGION env var',
+          'Maven S3 lookup error: missing region, check "AWS_REGION" variable',
         );
       });
 
@@ -127,7 +130,7 @@ describe('modules/datasource/maven/s3', () => {
           {
             failedUrl: 's3://repobucket/org/example/package/maven-metadata.xml',
           },
-          'S3 url not found',
+          'Maven S3 lookup error: object not found',
         );
       });
 
@@ -146,10 +149,23 @@ describe('modules/datasource/maven/s3', () => {
           {
             failedUrl: 's3://repobucket/org/example/package/maven-metadata.xml',
           },
-          'S3 url not found',
+          'Maven S3 lookup error: object not found',
         );
       });
 
+      it('returns null for Deleted marker', async () => {
+        s3mock
+          .on(GetObjectCommand, {
+            Bucket: 'repobucket',
+            Key: 'org/example/package/maven-metadata.xml',
+          })
+          .resolvesOnce({ DeleteMarker: true });
+
+        const res = await get('org.example:package', baseUrlS3);
+
+        expect(res).toBeNull();
+      });
+
       it('returns null for unknown error', async () => {
         s3mock
           .on(GetObjectCommand, {
@@ -163,10 +179,10 @@ describe('modules/datasource/maven/s3', () => {
         expect(res).toBeNull();
         expect(logger.debug).toHaveBeenCalledWith(
           {
+            err: expect.objectContaining({ message: 'Unknown error' }),
             failedUrl: 's3://repobucket/org/example/package/maven-metadata.xml',
-            message: 'Unknown error',
           },
-          'Unknown S3 download error',
+          'Maven S3 lookup error: unknown error',
         );
       });
 
@@ -178,9 +194,6 @@ describe('modules/datasource/maven/s3', () => {
           })
           .resolvesOnce({});
         expect(await get('org.example:package', baseUrlS3)).toBeNull();
-        expect(logger.debug).toHaveBeenCalledWith(
-          "Expecting Readable response type got 'undefined' type instead",
-        );
       });
     });
   });
diff --git a/lib/modules/datasource/maven/types.ts b/lib/modules/datasource/maven/types.ts
index 7698e49fbc66631f80af9902a7e8e1a6f85f1fbc..853326887eaadc2059dd5d1ef57d29b488f4a27c 100644
--- a/lib/modules/datasource/maven/types.ts
+++ b/lib/modules/datasource/maven/types.ts
@@ -1,4 +1,5 @@
 import type { XmlDocument } from 'xmldoc';
+import type { Result } from '../../../util/result';
 import type { ReleaseResult } from '../types';
 
 export interface MavenDependency {
@@ -19,3 +20,30 @@ export type DependencyInfo = Pick<
   ReleaseResult,
   'homepage' | 'sourceUrl' | 'packageScope'
 >;
+
+export interface MavenFetchSuccess<T = string> {
+  isCacheable?: boolean;
+  lastModified?: string;
+  data: T;
+}
+
+export type MavenFetchError =
+  | { type: 'invalid-url' }
+  | { type: 'host-disabled' }
+  | { type: 'not-found' }
+  | { type: 'host-error' }
+  | { type: 'permission-issue' }
+  | { type: 'temporary-error' }
+  | { type: 'maven-central-temporary-error'; err: Error }
+  | { type: 'connection-error' }
+  | { type: 'unsupported-host' }
+  | { type: 'unsupported-format' }
+  | { type: 'unsupported-protocol' }
+  | { type: 'credentials-error' }
+  | { type: 'missing-aws-region' }
+  | { type: 'unknown'; err: Error };
+
+export type MavenFetchResult<T = string> = Result<
+  MavenFetchSuccess<T>,
+  MavenFetchError
+>;
diff --git a/lib/modules/datasource/maven/util.spec.ts b/lib/modules/datasource/maven/util.spec.ts
index fc42f24610b2b7545b16b7a6c0db4b9283824422..0921b7de58be6b048b14961864696842c88ed89a 100644
--- a/lib/modules/datasource/maven/util.spec.ts
+++ b/lib/modules/datasource/maven/util.spec.ts
@@ -2,6 +2,7 @@ import type Request from 'got/dist/source/core';
 import { partial } from '../../../../test/util';
 import { HOST_DISABLED } from '../../../constants/error-messages';
 import { Http, HttpError } from '../../../util/http';
+import type { MavenFetchError } from './types';
 import {
   checkResource,
   downloadHttpProtocol,
@@ -55,9 +56,12 @@ describe('modules/datasource/maven/util', () => {
   });
 
   describe('downloadS3Protocol', () => {
-    it('returns null for non-S3 URLs', async () => {
+    it('fails for non-S3 URLs', async () => {
       const res = await downloadS3Protocol(new URL('http://not-s3.com/'));
-      expect(res).toBeNull();
+      expect(res.unwrap()).toEqual({
+        ok: false,
+        err: { type: 'invalid-url' } satisfies MavenFetchError,
+      });
     });
   });
 
diff --git a/lib/modules/datasource/maven/util.ts b/lib/modules/datasource/maven/util.ts
index b8db9f298c083668323d73b588df0c5728e0aa2f..3a6d85956b4c2c3986096e5cfb230ce64acdd617 100644
--- a/lib/modules/datasource/maven/util.ts
+++ b/lib/modules/datasource/maven/util.ts
@@ -20,6 +20,8 @@ import type {
   DependencyInfo,
   HttpResourceCheckResult,
   MavenDependency,
+  MavenFetchResult,
+  MavenFetchSuccess,
   MavenXml,
 } from './types';
 
@@ -142,42 +144,85 @@ function isS3NotFound(err: Error): boolean {
   return err.message === 'NotFound' || err.message === 'NoSuchKey';
 }
 
-export async function downloadS3Protocol(pkgUrl: URL): Promise<string | null> {
+export async function downloadS3Protocol(
+  pkgUrl: URL,
+): Promise<MavenFetchResult> {
   logger.trace({ url: pkgUrl.toString() }, `Attempting to load S3 dependency`);
-  try {
-    const s3Url = parseS3Url(pkgUrl);
-    if (s3Url === null) {
-      return null;
-    }
-    const { Body: res } = await getS3Client().send(new GetObjectCommand(s3Url));
-    if (res instanceof Readable) {
-      return streamToString(res);
-    }
-    logger.debug(
-      `Expecting Readable response type got '${typeof res}' type instead`,
-    );
-  } catch (err) {
-    const failedUrl = pkgUrl.toString();
-    if (err.name === 'CredentialsProviderError') {
-      logger.debug(
-        { failedUrl },
-        'Dependency lookup authorization failed. Please correct AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY env vars',
-      );
-    } else if (err.message === 'Region is missing') {
-      logger.debug(
-        { failedUrl },
-        'Dependency lookup failed. Please a correct AWS_REGION env var',
-      );
-    } else if (isS3NotFound(err)) {
-      logger.trace({ failedUrl }, `S3 url not found`);
-    } else {
-      logger.debug(
-        { failedUrl, message: err.message },
-        'Unknown S3 download error',
-      );
-    }
+
+  const s3Url = parseS3Url(pkgUrl);
+  if (!s3Url) {
+    return Result.err({ type: 'invalid-url' });
   }
-  return null;
+
+  return await Result.wrap(() => {
+    const command = new GetObjectCommand(s3Url);
+    const client = getS3Client();
+    return client.send(command);
+  })
+    .transform(
+      async ({
+        Body,
+        LastModified,
+        DeleteMarker,
+      }): Promise<MavenFetchResult> => {
+        if (DeleteMarker) {
+          logger.trace(
+            { failedUrl: pkgUrl.toString() },
+            'Maven S3 lookup error: DeleteMarker encountered',
+          );
+          return Result.err({ type: 'not-found' });
+        }
+
+        if (!(Body instanceof Readable)) {
+          logger.debug(
+            { failedUrl: pkgUrl.toString() },
+            'Maven S3 lookup error: unsupported Body type',
+          );
+          return Result.err({ type: 'unsupported-format' });
+        }
+
+        const data = await streamToString(Body);
+        const result: MavenFetchSuccess = { data };
+
+        const lastModified = normalizeDate(LastModified);
+        if (lastModified) {
+          result.lastModified = lastModified;
+        }
+
+        return Result.ok(result);
+      },
+    )
+    .catch((err): MavenFetchResult => {
+      if (!(err instanceof Error)) {
+        return Result.err(err);
+      }
+
+      const failedUrl = pkgUrl.toString();
+
+      if (err.name === 'CredentialsProviderError') {
+        logger.debug(
+          { failedUrl },
+          'Maven S3 lookup error: credentials provider error, check "AWS_ACCESS_KEY_ID" and "AWS_SECRET_ACCESS_KEY" variables',
+        );
+        return Result.err({ type: 'credentials-error' });
+      }
+
+      if (err.message === 'Region is missing') {
+        logger.debug(
+          { failedUrl },
+          'Maven S3 lookup error: missing region, check "AWS_REGION" variable',
+        );
+        return Result.err({ type: 'missing-aws-region' });
+      }
+
+      if (isS3NotFound(err)) {
+        logger.trace({ failedUrl }, 'Maven S3 lookup error: object not found');
+        return Result.err({ type: 'not-found' });
+      }
+
+      logger.debug({ failedUrl, err }, 'Maven S3 lookup error: unknown error');
+      return Result.err({ type: 'unknown', err });
+    });
 }
 
 export async function downloadArtifactRegistryProtocol(
@@ -334,10 +379,12 @@ export async function downloadMavenXml(
   }
 
   if (protocol === 's3:') {
-    const res = await downloadS3Protocol(pkgUrl);
-    if (res) {
-      return { xml: new XmlDocument(res) };
-    }
+    const rawResult = await downloadS3Protocol(pkgUrl);
+    const xmlResult = rawResult.transform(({ isCacheable, data }): MavenXml => {
+      const xml = new XmlDocument(data);
+      return { xml };
+    });
+    return xmlResult.unwrapOr({});
   }
 
   logger.debug(