Handle version not parsing gracefully (#3929)

diff --git a/lib/src/lock_file.dart b/lib/src/lock_file.dart
index f725260..48de2fd 100644
--- a/lib/src/lock_file.dart
+++ b/lib/src/lock_file.dart
@@ -168,8 +168,9 @@
         packageEntries,
         (name, spec) {
           // Parse the version.
-          final versionEntry = _getStringEntry(spec, 'version');
-          final version = Version.parse(versionEntry);
+          final versionEntry =
+              _getEntry<YamlScalar>(spec, 'version', 'version string');
+          final version = _parseVersion(versionEntry);
 
           // Parse the source.
           final sourceName = _getStringEntry(spec, 'source');
@@ -243,7 +244,7 @@
       return fn();
     } on FormatException catch (e) {
       throw SourceSpanFormatException(
-        'Invalid $description: ${e.message}',
+        '$description: ${e.message}',
         span,
       );
     }
@@ -257,6 +258,14 @@
     );
   }
 
+  static Version _parseVersion(YamlNode node) {
+    return _parseNode(
+      node,
+      'version',
+      parse: Version.parse,
+    );
+  }
+
   static String _getStringEntry(YamlMap map, String key) {
     return _parseNode<String>(
       _getEntry<YamlScalar>(map, key, 'string'),
@@ -275,19 +284,19 @@
       final value = node.value;
       if (parse != null) {
         if (value is! String) {
-          _failAt('Expected a $typeDescription.', node);
+          _failAt('Expected a $typeDescription', node);
         }
         return _wrapFormatException(
-          'Expected a $typeDescription.',
+          'Expected a $typeDescription',
           node.span,
           () => parse(node.value),
         );
       } else if (value is T) {
         return value;
       }
-      _failAt('Expected a $typeDescription.', node);
+      _failAt('Expected a $typeDescription', node);
     }
-    _failAt('Expected a $typeDescription.', node);
+    _failAt('Expected a $typeDescription', node);
   }
 
   static void _parseEachEntry<K, V>(
diff --git a/test/lock_file_test.dart b/test/lock_file_test.dart
index 3dbcf92..9fa7081 100644
--- a/test/lock_file_test.dart
+++ b/test/lock_file_test.dart
@@ -7,6 +7,7 @@
 import 'package:pub/src/source/hosted.dart';
 import 'package:pub/src/system_cache.dart';
 import 'package:pub_semver/pub_semver.dart';
+import 'package:source_span/source_span.dart';
 import 'package:test/test.dart' hide Description;
 import 'package:yaml/yaml.dart';
 
@@ -135,7 +136,7 @@
               sources,
             );
           },
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
@@ -149,7 +150,7 @@
               sources,
             );
           },
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
@@ -166,7 +167,7 @@
               sources,
             );
           },
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
@@ -184,7 +185,7 @@
               sources,
             );
           },
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
@@ -201,7 +202,7 @@
               sources,
             );
           },
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
@@ -218,7 +219,7 @@
               sources,
             );
           },
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
@@ -236,54 +237,54 @@
               sources,
             );
           },
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
       test("throws if the old-style SDK constraint isn't a string", () {
         expect(
           () => LockFile.parse('sdk: 1.0', sources),
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
       test('throws if the old-style SDK constraint is invalid', () {
         expect(
           () => LockFile.parse('sdk: oops', sources),
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
       test("throws if the sdks field isn't a map", () {
         expect(
           () => LockFile.parse('sdks: oops', sources),
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
       test("throws if an sdk constraint isn't a string", () {
         expect(
           () => LockFile.parse('sdks: {dart: 1.0}', sources),
-          throwsFormatException,
+          throwsSourceSpanException,
         );
         expect(
           () {
             LockFile.parse('sdks: {dart: 1.0.0, flutter: 1.0}', sources);
           },
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
       test('throws if an sdk constraint is invalid', () {
         expect(
           () => LockFile.parse('sdks: {dart: oops}', sources),
-          throwsFormatException,
+          throwsSourceSpanException,
         );
         expect(
           () {
             LockFile.parse('sdks: {dart: 1.0.0, flutter: oops}', sources);
           },
-          throwsFormatException,
+          throwsSourceSpanException,
         );
       });
 
@@ -411,3 +412,5 @@
     });
   });
 }
+
+final throwsSourceSpanException = throwsA(isA<SourceSpanException>());