diff --git a/distribution/changelog.txt b/distribution/changelog.txt
index e5198e81f1..22a3ca8a08 100644
--- a/distribution/changelog.txt
+++ b/distribution/changelog.txt
@@ -54,6 +54,7 @@
- Fix: [#19379] “No platforms” station style shows platforms on the Junior Roller Coaster.
- Fix: [#19380] Startup crash when no sequences are installed and random sequences are enabled.
- Fix: [#19391] String corruption caused by an improper buffer handling in ‘GfxWrapString’.
+- Fix: [#19434, #19509] Object types added by OpenRCT2 do not get removed when executing ‘remove_unused_objects’.
- Fix: [#19475] Cannot increase loan when more than £1000 in debt.
- Fix: [#19493] SV4 saves not importing the correct vehicle colours.
- Fix: [#19517] Crash when peeps try to exit or enter hacked rides that have no waypoints specified.
diff --git a/src/openrct2/EditorObjectSelectionSession.cpp b/src/openrct2/EditorObjectSelectionSession.cpp
index e7bd586340..97db7b17c7 100644
--- a/src/openrct2/EditorObjectSelectionSession.cpp
+++ b/src/openrct2/EditorObjectSelectionSession.cpp
@@ -671,11 +671,13 @@ int32_t EditorRemoveUnusedObjects()
{
const ObjectRepositoryItem* item = &items[i];
ObjectType objectType = item->Type;
-
- if (objectType >= ObjectType::SceneryGroup)
- {
+ if (ObjectTypeIsIntransient(objectType))
+ continue;
+
+ // These object types require exactly one object to be selected at all times.
+ // Removing that object can badly break the game state.
+ if (objectType == ObjectType::ParkEntrance || objectType == ObjectType::Water)
continue;
- }
_numSelectedObjectsForType[EnumValue(objectType)]--;
_objectSelectionFlags[i] &= ~ObjectSelectionFlags::Selected;
diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index 822da7fe62..b86b45e203 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -814,6 +814,7 @@
+
diff --git a/src/openrct2/object/ObjectTypes.cpp b/src/openrct2/object/ObjectTypes.cpp
new file mode 100644
index 0000000000..e9988e7053
--- /dev/null
+++ b/src/openrct2/object/ObjectTypes.cpp
@@ -0,0 +1,22 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2023 OpenRCT2 developers
+ *
+ * For a complete list of all authors, please refer to contributors.md
+ * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
+ *
+ * OpenRCT2 is licensed under the GNU General Public License version 3.
+ *****************************************************************************/
+
+#include "ObjectTypes.h"
+
+#include
+
+bool ObjectTypeIsTransient(ObjectType type)
+{
+ return std::find(TransientObjectTypes.begin(), TransientObjectTypes.end(), type) != std::end(TransientObjectTypes);
+}
+
+bool ObjectTypeIsIntransient(ObjectType type)
+{
+ return std::find(IntransientObjectTypes.begin(), IntransientObjectTypes.end(), type) != std::end(IntransientObjectTypes);
+}
diff --git a/src/openrct2/object/ObjectTypes.h b/src/openrct2/object/ObjectTypes.h
index 5f6aa2f181..05444663a9 100644
--- a/src/openrct2/object/ObjectTypes.h
+++ b/src/openrct2/object/ObjectTypes.h
@@ -72,3 +72,9 @@ constexpr std::array TransientObjectTypes = {
ObjectType::ParkEntrance, ObjectType::Water, ObjectType::TerrainSurface, ObjectType::TerrainEdge,
ObjectType::Station, ObjectType::Music, ObjectType::FootpathSurface, ObjectType::FootpathRailings,
};
+
+// Object types that cannot be saved in a park file.
+constexpr std::array IntransientObjectTypes = { ObjectType::ScenarioText, ObjectType::Audio };
+
+bool ObjectTypeIsTransient(ObjectType type);
+bool ObjectTypeIsIntransient(ObjectType type);