diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java
index 36307e7bd..aef8cf8b2 100644
--- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java
+++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java
@@ -33,6 +33,7 @@ import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
 import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
 import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
 import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
+import org.geysermc.geyser.GeyserImpl;
 import org.geysermc.geyser.inventory.GeyserItemStack;
 import org.geysermc.geyser.inventory.Inventory;
 import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe;
@@ -66,27 +67,41 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
         if (inventory == null)
             return;
 
-        // Intentional behavior here below the cursor; Minecraft 1.18.1 also does this.
-        int stateId = packet.getStateId();
-        session.setEmulatePost1_16Logic(stateId > 0 || stateId != inventory.getStateId());
-        inventory.setStateId(stateId);
-
         InventoryTranslator translator = session.getInventoryTranslator();
         if (translator != null) {
             if (session.getCraftingGridFuture() != null) {
                 session.getCraftingGridFuture().cancel(false);
             }
-            updateCraftingGrid(session, packet.getSlot(), packet.getItem(), inventory, translator);
+
+            int slot = packet.getSlot();
+            if (slot >= inventory.getSize()) {
+                GeyserImpl geyser = session.getGeyser();
+                geyser.getLogger().warning("ClientboundContainerSetSlotPacket sent to " + session.name()
+                        + " that exceeds inventory size!");
+                if (geyser.getConfig().isDebugMode()) {
+                    geyser.getLogger().debug(packet);
+                    geyser.getLogger().debug(inventory);
+                }
+                // 1.19.0 behavior: the state ID will not be set due to exception
+                return;
+            }
+
+            updateCraftingGrid(session, slot, packet.getItem(), inventory, translator);
 
             GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
             if (packet.getContainerId() == 0 && !(translator instanceof PlayerInventoryTranslator)) {
                 // In rare cases, the window ID can still be 0 but Java treats it as valid
-                session.getPlayerInventory().setItem(packet.getSlot(), newItem, session);
-                InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), packet.getSlot());
+                session.getPlayerInventory().setItem(slot, newItem, session);
+                InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), slot);
             } else {
-                inventory.setItem(packet.getSlot(), newItem, session);
-                translator.updateSlot(session, inventory, packet.getSlot());
+                inventory.setItem(slot, newItem, session);
+                translator.updateSlot(session, inventory, slot);
             }
+
+            // Intentional behavior here below the cursor; Minecraft 1.18.1 also does this.
+            int stateId = packet.getStateId();
+            session.setEmulatePost1_16Logic(stateId > 0 || stateId != inventory.getStateId());
+            inventory.setStateId(stateId);
         }
     }