diff --git a/packages/shorebird_cli/lib/src/cache.dart b/packages/shorebird_cli/lib/src/cache.dart
index 7075acb83..87da25520 100644
--- a/packages/shorebird_cli/lib/src/cache.dart
+++ b/packages/shorebird_cli/lib/src/cache.dart
@@ -7,6 +7,7 @@ import 'package:path/path.dart' as p;
 import 'package:platform/platform.dart';
 import 'package:scoped/scoped.dart';
 import 'package:shorebird_cli/src/http_client/http_client.dart';
+import 'package:shorebird_cli/src/logger.dart';
 import 'package:shorebird_cli/src/platform.dart';
 import 'package:shorebird_cli/src/process.dart';
 import 'package:shorebird_cli/src/shorebird_env.dart';
@@ -53,6 +54,7 @@ class Cache {
   }) : httpClient = httpClient ?? retryingHttpClient(http.Client()) {
     registerArtifact(PatchArtifact(cache: this, platform: platform));
     registerArtifact(BundleToolArtifact(cache: this, platform: platform));
+    registerArtifact(ShorebirdLinkerArtifact(cache: this, platform: platform));
   }
 
   final http.Client httpClient;
@@ -133,6 +135,8 @@ abstract class CachedArtifact {
 
   List<String> get executables => [];
 
+  bool get required => true;
+
   Future<void> extractArtifact(http.ByteStream stream, String outputPath) {
     final file = File(p.join(outputPath, name))..createSync(recursive: true);
     return stream.pipe(file.openWrite());
@@ -157,6 +161,13 @@ allowed to access $storageUrl.''',
     }
 
     if (response.statusCode != HttpStatus.ok) {
+      if (!required && response.statusCode == HttpStatus.notFound) {
+        logger.detail(
+          '[cache] optional artifact: "$name" was not found, skipping...',
+        );
+        return;
+      }
+
       throw CacheUpdateFailure(
         '''Failed to download $name: ${response.statusCode} ${response.reasonPhrase}''',
       );
@@ -176,6 +187,34 @@ allowed to access $storageUrl.''',
   }
 }
 
+class ShorebirdLinkerArtifact extends CachedArtifact {
+  ShorebirdLinkerArtifact({required super.cache, required super.platform});
+
+  @override
+  String get name => 'shorebird_linker';
+
+  @override
+  List<String> get executables => ['shorebird_linker'];
+
+  /// The linker is only available for revisions that support mixed-mode.
+  @override
+  bool get required => false;
+
+  @override
+  String get storageUrl {
+    var artifactName = 'linker-';
+    if (platform.isMacOS) {
+      artifactName += 'darwin-x64';
+    } else if (platform.isLinux) {
+      artifactName += 'linux-x64';
+    } else if (platform.isWindows) {
+      artifactName += 'windows-x64';
+    }
+
+    return '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${shorebirdEnv.shorebirdEngineRevision}/$artifactName';
+  }
+}
+
 class PatchArtifact extends CachedArtifact {
   PatchArtifact({required super.cache, required super.platform});
 
diff --git a/packages/shorebird_cli/test/src/cache_test.dart b/packages/shorebird_cli/test/src/cache_test.dart
index e1da00c3c..0fae23dc6 100644
--- a/packages/shorebird_cli/test/src/cache_test.dart
+++ b/packages/shorebird_cli/test/src/cache_test.dart
@@ -8,6 +8,7 @@ import 'package:path/path.dart' as p;
 import 'package:platform/platform.dart';
 import 'package:scoped/scoped.dart';
 import 'package:shorebird_cli/src/cache.dart';
+import 'package:shorebird_cli/src/logger.dart';
 import 'package:shorebird_cli/src/platform.dart';
 import 'package:shorebird_cli/src/process.dart';
 import 'package:shorebird_cli/src/shorebird_env.dart';
@@ -32,6 +33,7 @@ void main() {
 
     late Directory shorebirdRoot;
     late http.Client httpClient;
+    late Logger logger;
     late Platform platform;
     late Process chmodProcess;
     late ShorebirdEnv shorebirdEnv;
@@ -43,6 +45,7 @@ void main() {
         () => body(),
         values: {
           cacheRef.overrideWith(() => cache),
+          loggerRef.overrideWith(() => logger),
           platformRef.overrideWith(() => platform),
           processRef.overrideWith(() => shorebirdProcess),
           shorebirdEnvRef.overrideWith(() => shorebirdEnv),
@@ -56,6 +59,7 @@ void main() {
 
     setUp(() {
       httpClient = MockHttpClient();
+      logger = MockLogger();
       platform = MockPlatform();
       chmodProcess = MockProcess();
       shorebirdEnv = MockShorebirdEnv();
@@ -206,6 +210,35 @@ void main() {
           );
         });
 
+        test('skips optional artifacts if a 404 is returned', () async {
+          when(() => httpClient.send(any())).thenAnswer(
+            (invocation) async {
+              final request =
+                  invocation.positionalArguments.first as http.BaseRequest;
+              if (request.url.path.endsWith('linker-darwin-x64')) {
+                return http.StreamedResponse(
+                  const Stream.empty(),
+                  HttpStatus.notFound,
+                  reasonPhrase: 'Not Found',
+                );
+              }
+              return http.StreamedResponse(
+                Stream.value(ZipEncoder().encode(Archive())!),
+                HttpStatus.ok,
+              );
+            },
+          );
+          await expectLater(
+            runWithOverrides(cache.updateAll),
+            completes,
+          );
+          verify(
+            () => logger.detail(
+              '''[cache] optional artifact: "shorebird_linker" was not found, skipping...''',
+            ),
+          ).called(1);
+        });
+
         test('downloads correct artifacts', () async {
           final patchArtifactDirectory = runWithOverrides(
             () => cache.getArtifactDirectory('patch'),