diff --git a/src/api/course.py b/src/api/course.py
index 56473d4c3e95ab609acb6f89440dd40cbbb81656..56b0a223973ecce4e8313a9e3ec40f31200e92a9 100644
--- a/src/api/course.py
+++ b/src/api/course.py
@@ -24,9 +24,9 @@ COURSE_SECONDARY_DB_SELECTION = f"""
 
 
 def _semester_db_to_json(semester_db: str):
-    if semester_db is None or semester_db.isspace():
+    if semester_db is None or len(semester_db) == 0 or semester_db.isspace():
         return "none"  # pragma: no cover
-    from api.objects.types import SEMESTER_STRING_PATTERN
+    from api.objects.type import SEMESTER_STRING_PATTERN
     if SEMESTER_STRING_PATTERN.fullmatch(semester_db) is None:  # pragma: no cover
         print(f"Warning: Invalid semester string in database: {truncate_string(semester_db)}")
         return "none"
diff --git a/src/api/objects/changelog_v1.py b/src/api/objects/changelog_v1.py
index 5f24db19b0aa6865915f9f7c134507f22a7206ca..6e5a11320b60ea3e4e045f7a58a7e4a392d006b3 100644
--- a/src/api/objects/changelog_v1.py
+++ b/src/api/objects/changelog_v1.py
@@ -6,6 +6,7 @@ from api.objects.changelog import ChangelogVersionBuilder, ChangelogEntryError,
 from api.objects.changelog_entries import ModificationEntry, DeletionEntry
 from api.objects.field import ObjectField
 from api.objects.objects import *
+from api.objects.type import SEMESTER_STRING_PATTERN
 
 
 def _encode_identity(value_json: str) -> str:
@@ -86,11 +87,21 @@ def _decode_legacy_string_to_id_string(legacy_value: str) -> str:
 def _decode_legacy_string_to_semester_string(legacy_value: str) -> str:
     if legacy_value is None:
         raise ChangelogEntryError("Cannot encode null values")
+    if len(legacy_value) == 0 or legacy_value.isspace():
+        return "none"
     if len(legacy_value) > 100 or SEMESTER_STRING_PATTERN.fullmatch(legacy_value) is None:
         raise ChangelogEntryError(f"Legacy string is not a semester string: '{truncate_string(legacy_value)}'")
     return legacy_value
 
 
+def _encode_legacy_semester_string(value_json: str) -> str:
+    if value_json is None:
+        raise ChangelogEntryError("Cannot encode null values")
+    if value_json == "none":
+        return ""
+    return value_json
+
+
 def _decode_legacy_announcement_level(legacy_value: str) -> str:
     level = _decode_legacy_integer_non_negative(legacy_value)
     match level:
@@ -164,7 +175,7 @@ class _DeletionEntryCodec(ChangelogEntryCodec[SimpleChangelogEntrySubject]):
 _CODEC_BOOLEAN = _LegacyModificationEntryCodec(_decode_legacy_boolean, _encode_legacy_boolean)
 _CODEC_STRING_SHORT = _LegacyModificationEntryCodec(_decode_legacy_shortstring_to_string_short, _encode_identity)
 _CODEC_STRING_TO_ID_STRING = _LegacyModificationEntryCodec(_decode_legacy_string_to_id_string, _encode_identity)
-_CODEC_STRING_TO_SEMESTER_STRING = _LegacyModificationEntryCodec(_decode_legacy_string_to_semester_string, _encode_identity)
+_CODEC_STRING_TO_SEMESTER_STRING = _LegacyModificationEntryCodec(_decode_legacy_string_to_semester_string, _encode_legacy_semester_string)
 _CODEC_TEXT_TO_STRING_LONG = _LegacyModificationEntryCodec(_decode_legacy_text_to_string_long, _encode_identity)
 _CODEC_DATETIME = _LegacyModificationEntryCodec(_decode_legacy_datetime, _encode_legacy_datetime)
 _CODEC_INTEGER_NON_NEGATIVE = _LegacyModificationEntryCodec(_decode_legacy_integer_non_negative, _encode_legacy_integer)
diff --git a/src/api/objects/type.py b/src/api/objects/type.py
index ccea4051a3aa9f41716e95cacccb7cd6965a1524..0d0fd9cdd4cc0c6944ec04d8fa2ead4f53dd8b17 100644
--- a/src/api/objects/type.py
+++ b/src/api/objects/type.py
@@ -308,6 +308,39 @@ class MappedStringType(AbstractStringType):
         return self.__json_to_db[string]
 
 
+SEMESTER_STRING_REGEX = "([0-9]{4}(ws|ss)|none)"
+SEMESTER_STRING_PATTERN = re.compile(SEMESTER_STRING_REGEX)
+
+
+class SemesterStringType(AbstractStringType):
+    
+    def __init__(self):
+        super().__init__("semester_string", False)
+    
+    def get_max_string_length(self) -> int:
+        """Returns the exact maximum length of this string"""
+        return 6
+    
+    def db_value_to_json(self, db_value: str) -> str:
+        if len(db_value) == 0 or db_value.isspace():
+            return "none"
+        if SEMESTER_STRING_PATTERN.fullmatch(db_value) is None:
+            print(f"Warning: Invalid semester string in database: {truncate_string(db_value)}")
+            return "none"
+        return db_value
+    
+    def validate_and_convert_client_value(self,
+                                          transaction: AbstractTransaction,
+                                          object_id: int or None,
+                                          client_value: CJsonValue) -> str or None:
+        value = client_value.as_string(50, 0)
+        if SEMESTER_STRING_PATTERN.fullmatch(value) is None:
+            client_value.raise_error("String does not match pattern")
+        if value == "none":
+            value = ""
+        return value
+    
+
 class DatetimeType(AbstractStringType):
     
     def __init__(self, may_be_none: bool = False):
diff --git a/src/api/objects/types.py b/src/api/objects/types.py
index 9dbe2d43e16701af763180c36f27d3995b211652..62faf7954713543ab4463110aacaf14661ab0c1f 100644
--- a/src/api/objects/types.py
+++ b/src/api/objects/types.py
@@ -1,14 +1,10 @@
-import re
 
 from api.miscellaneous import ID_STRING_REGEX_NO_LENGTH
-from api.objects.type import (NumberType, IntType, LongType,
+from api.objects.type import (IntType, LongType,
                               SimpleType, ObjectIdType,
-                              StringType, SimpleStringType, UniqueStringType, MappedStringType,
+                              StringType, SimpleStringType, UniqueStringType, MappedStringType, SemesterStringType,
                               DatetimeType)
 
-SEMESTER_STRING_REGEX = "([0-9]{4}(ws|ss)|none)"
-SEMESTER_STRING_PATTERN = re.compile(SEMESTER_STRING_REGEX)
-
 TYPE_INT = IntType()
 TYPE_LONG = LongType()
 TYPE_BOOLEAN = SimpleType("boolean", lambda db: bool(db), lambda json_value: json_value.as_bool())
@@ -24,7 +20,7 @@ TYPE_COURSE_ID_STRING = UniqueStringType(
     "id",
     "handle"
 )
-TYPE_SEMESTER_STRING = SimpleStringType("semester_string", 6, 0, SEMESTER_STRING_REGEX)  # Max length for safety
+TYPE_SEMESTER_STRING = SemesterStringType()
 TYPE_DURATION = IntType(0, 2147483647)
 TYPE_DATETIME = DatetimeType()
 TYPE_TIMESTAMP = IntType(0, 2147483647)