diff --git a/libaegisub/common/vfr.cpp b/libaegisub/common/vfr.cpp
index 6b6911bb99cec500d29e2bd3abd31908f61e768c..31480248cab8b15ec744941902e3270cae6d6410 100644
--- a/libaegisub/common/vfr.cpp
+++ b/libaegisub/common/vfr.cpp
@@ -38,9 +38,11 @@ using namespace agi::vfr;
 /// @param timecodes List of timecodes to check
 void validate_timecodes(std::vector<int> const& timecodes) {
 	if (timecodes.size() <= 1)
-		throw TooFewTimecodes("Must have at least two timecodes to do anything useful");
+		throw InvalidFramerate("Must have at least two timecodes to do anything useful");
 	if (!is_sorted(timecodes.begin(), timecodes.end()))
-		throw UnorderedTimecodes("Timecodes are out of order");
+		throw InvalidFramerate("Timecodes are out of order");
+	if (timecodes.front() == timecodes.back())
+		throw InvalidFramerate("Timecodes are all identical");
 }
 
 /// @brief Shift timecodes so that frame 0 starts at time 0
@@ -71,15 +73,15 @@ TimecodeRange v1_parse_line(std::string const& str) {
 	if (ss.fail() || comma1 != ',' || comma2 != ',' || !ss.eof())
 		throw MalformedLine(str);
 	if (range.start < 0 || range.end < 0)
-		throw UnorderedTimecodes("Cannot specify frame rate for negative frames.");
+		throw InvalidFramerate("Cannot specify frame rate for negative frames.");
 	if (range.end < range.start)
-		throw UnorderedTimecodes("End frame must be greater than or equal to start frame");
+		throw InvalidFramerate("End frame must be greater than or equal to start frame");
 	if (range.fps <= 0.)
-		throw BadFPS("FPS must be greater than zero");
+		throw InvalidFramerate("FPS must be greater than zero");
 	if (range.fps > 1000.)
 		// This is our limitation, not mkvmerge's
 		// mkvmerge uses nanoseconds internally
-		throw BadFPS("FPS must be at most 1000");
+		throw InvalidFramerate("FPS must be at most 1000");
 	return range;
 }
 
@@ -91,8 +93,8 @@ TimecodeRange v1_parse_line(std::string const& str) {
 /// @return Assumed fps times one million
 int64_t v1_parse(line_iterator<std::string> file, std::string line, std::vector<int> &timecodes, int64_t &last) {
 	double fps = atof(line.substr(7).c_str());
-	if (fps <= 0.) throw BadFPS("Assumed FPS must be greater than zero");
-	if (fps > 1000.) throw BadFPS("Assumed FPS must not be greater than 1000");
+	if (fps <= 0.) throw InvalidFramerate("Assumed FPS must be greater than zero");
+	if (fps > 1000.) throw InvalidFramerate("Assumed FPS must not be greater than 1000");
 
 	std::vector<TimecodeRange> ranges;
 	for (auto const& line : file) {
@@ -111,7 +113,7 @@ int64_t v1_parse(line_iterator<std::string> file, std::string line, std::vector<
 		if (frame > range.start) {
 			// mkvmerge allows overlapping timecode ranges, but does completely
 			// broken things with them
-			throw UnorderedTimecodes("Override ranges must not overlap");
+			throw InvalidFramerate("Override ranges must not overlap");
 		}
 		for (; frame < range.start; ++frame) {
 			timecodes.push_back(int(time + .5));
@@ -133,8 +135,8 @@ Framerate::Framerate(double fps)
 : denominator(default_denominator)
 , numerator(int64_t(fps * denominator))
 {
-	if (fps < 0.) throw BadFPS("FPS must be greater than zero");
-	if (fps > 1000.) throw BadFPS("FPS must not be greater than 1000");
+	if (fps < 0.) throw InvalidFramerate("FPS must be greater than zero");
+	if (fps > 1000.) throw InvalidFramerate("FPS must not be greater than 1000");
 	timecodes.push_back(0);
 }
 
@@ -144,8 +146,8 @@ Framerate::Framerate(int64_t numerator, int64_t denominator, bool drop)
 , drop(drop && numerator % denominator != 0)
 {
 	if (numerator <= 0 || denominator <= 0)
-		throw BadFPS("Numerator and denominator must both be greater than zero");
-	if (numerator / denominator > 1000) throw BadFPS("FPS must not be greater than 1000");
+		throw InvalidFramerate("Numerator and denominator must both be greater than zero");
+	if (numerator / denominator > 1000) throw InvalidFramerate("FPS must not be greater than 1000");
 	timecodes.push_back(0);
 }
 
diff --git a/libaegisub/include/libaegisub/vfr.h b/libaegisub/include/libaegisub/vfr.h
index 1658539123806f2fe865e5aff3d029ac055b0ab9..d929154fba415252cb511806ca7c9d521811faa9 100644
--- a/libaegisub/include/libaegisub/vfr.h
+++ b/libaegisub/include/libaegisub/vfr.h
@@ -41,15 +41,11 @@ enum Time {
 
 DEFINE_EXCEPTION(Error, Exception);
 /// FPS specified is not a valid frame rate
-DEFINE_EXCEPTION(BadFPS, Error);
+DEFINE_EXCEPTION(InvalidFramerate, Error);
 /// Unknown timecode file format
 DEFINE_EXCEPTION(UnknownFormat, Error);
 /// Invalid line encountered in a timecode file
 DEFINE_EXCEPTION(MalformedLine, Error);
-/// Timecode file or vector has too few timecodes to be usable
-DEFINE_EXCEPTION(TooFewTimecodes, Error);
-/// Timecode file or vector has timecodes that are not monotonically increasing
-DEFINE_EXCEPTION(UnorderedTimecodes, Error);
 
 /// @class Framerate
 /// @brief Class for managing everything related to converting frames to times
diff --git a/tests/tests/vfr.cpp b/tests/tests/vfr.cpp
index 2664d6d1fdb9bf17c5047f5ecbd2b1fc89b3ecf8..a9b8f3acf46a707c5d60b1a144aa72828fc7b4c8 100644
--- a/tests/tests/vfr.cpp
+++ b/tests/tests/vfr.cpp
@@ -43,14 +43,15 @@ TEST(lagi_vfr, constructors_good) {
 }
 
 TEST(lagi_vfr, constructors_bad_cfr) {
-	EXPECT_THROW(Framerate(-1.), BadFPS);
-	EXPECT_THROW(Framerate(1000.1), BadFPS);
+	EXPECT_THROW(Framerate(-1.), InvalidFramerate);
+	EXPECT_THROW(Framerate(1000.1), InvalidFramerate);
 }
 
 TEST(lagi_vfr, constructors_bad_timecodes) {
-	EXPECT_THROW(Framerate(std::initializer_list<int>{}), TooFewTimecodes);
-	EXPECT_THROW(Framerate({ 0 }), TooFewTimecodes);
-	EXPECT_THROW(Framerate({ 10, 0 }), UnorderedTimecodes);
+	EXPECT_THROW(Framerate(std::initializer_list<int>{}), InvalidFramerate);
+	EXPECT_THROW(Framerate({0}), InvalidFramerate);
+	EXPECT_THROW(Framerate({10, 0}), InvalidFramerate);
+	EXPECT_THROW(Framerate({0, 0}), InvalidFramerate);
 }
 
 TEST(lagi_vfr, constructors_bad_v1) {
@@ -58,18 +59,18 @@ TEST(lagi_vfr, constructors_bad_v1) {
 	EXPECT_THROW(Framerate("data/vfr/in/v1_too_few_parts.txt"), MalformedLine);
 	EXPECT_THROW(Framerate("data/vfr/in/v1_too_many_parts.txt"), MalformedLine);
 	EXPECT_THROW(Framerate("data/vfr/in/v1_float_frame_number.txt"), MalformedLine);
-	EXPECT_THROW(Framerate("data/vfr/in/v1_start_end_overlap.txt"), UnorderedTimecodes);
-	EXPECT_THROW(Framerate("data/vfr/in/v1_fully_contained.txt"), UnorderedTimecodes);
-	EXPECT_THROW(Framerate("data/vfr/in/v1_assume_over_1000.txt"), BadFPS);
-	EXPECT_THROW(Framerate("data/vfr/in/v1_override_over_1000.txt"), BadFPS);
-	EXPECT_THROW(Framerate("data/vfr/in/v1_override_zero.txt"), BadFPS);
-	EXPECT_THROW(Framerate("data/vfr/in/v1_negative_start_of_range.txt"), UnorderedTimecodes);
-	EXPECT_THROW(Framerate("data/vfr/in/v1_end_less_than_start.txt"), UnorderedTimecodes);
+	EXPECT_THROW(Framerate("data/vfr/in/v1_start_end_overlap.txt"), InvalidFramerate);
+	EXPECT_THROW(Framerate("data/vfr/in/v1_fully_contained.txt"), InvalidFramerate);
+	EXPECT_THROW(Framerate("data/vfr/in/v1_assume_over_1000.txt"), InvalidFramerate);
+	EXPECT_THROW(Framerate("data/vfr/in/v1_override_over_1000.txt"), InvalidFramerate);
+	EXPECT_THROW(Framerate("data/vfr/in/v1_override_zero.txt"), InvalidFramerate);
+	EXPECT_THROW(Framerate("data/vfr/in/v1_negative_start_of_range.txt"), InvalidFramerate);
+	EXPECT_THROW(Framerate("data/vfr/in/v1_end_less_than_start.txt"), InvalidFramerate);
 }
 
 TEST(lagi_vfr, constructors_bad_v2) {
-	EXPECT_THROW(Framerate("data/vfr/in/v2_empty.txt"), TooFewTimecodes);
-	EXPECT_THROW(Framerate("data/vfr/in/v2_out_of_order.txt"), UnorderedTimecodes);
+	EXPECT_THROW(Framerate("data/vfr/in/v2_empty.txt"), InvalidFramerate);
+	EXPECT_THROW(Framerate("data/vfr/in/v2_out_of_order.txt"), InvalidFramerate);
 
 	EXPECT_THROW(Framerate("data/vfr/in/empty.txt"), UnknownFormat);
 }