From c4577ed46fb357cd656810441de0646c64961abf Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sat, 7 Feb 2026 19:32:27 -0600 Subject: [PATCH 1/2] fix: validate player positions in lineup before game start Prevents PositionNotFoundException from crashing mlb-campaign when a player is placed at a position they cannot play (e.g. an outfielder listed at Catcher in the Google Sheet). Adds early validation in get_lineups_from_sheets and proper error handling at all read_lineup call sites so the user gets a clear message and the game is cleaned up. Co-Authored-By: Claude Opus 4.6 --- cogs/gameplay.py | 30 ++++++++++++++++++++++++------ command_logic/logic_gameplay.py | 21 ++++++++++++++++++++- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/cogs/gameplay.py b/cogs/gameplay.py index 8094e4b..752a560 100644 --- a/cogs/gameplay.py +++ b/cogs/gameplay.py @@ -436,19 +436,26 @@ class Gameplay(commands.Cog): await interaction.channel.send(content=f'### {human_team.lname} Starting Pitcher', view=sp_view) try: - await asyncio.sleep(5) + await asyncio.sleep(5) this_play = await read_lineup( - session, - interaction, + session, + interaction, this_game=this_game, lineup_team=human_team, sheets_auth=self.sheets, lineup_num=1 if roster_choice == 'vs Right' else 2, league_name=this_game.game_type ) + except PositionNotFoundException as e: + logger.error(f'Position validation failed during lineup load: {e}') + this_game.active = False + session.add(this_game) + session.commit() + await interaction.channel.send(content=str(e)) + return except LineupsMissingException as e: logger.error(f'Attempting to start game, pausing for 5 seconds: {e}') - await asyncio.sleep(5) + await asyncio.sleep(5) try: this_play = this_game.current_play_or_none(session) @@ -668,6 +675,13 @@ class Gameplay(commands.Cog): lineup_num=1 if roster_choice == 'vs Right' else 2, league_name=this_game.game_type ) + except PositionNotFoundException as e: + logger.error(f'Position validation failed during lineup load: {e}') + this_game.active = False + session.add(this_game) + session.commit() + await interaction.channel.send(content=str(e)) + return except LineupsMissingException as e: # Expected - can't initialize play without SP yet logger.info(f'Field player lineup read from sheets, waiting for SP selection: {e}') @@ -1044,14 +1058,18 @@ class Gameplay(commands.Cog): logger.info(f'lineup: {lineup} / value: {lineup.value} / name: {lineup.name}') try: this_play = await read_lineup( - session, - interaction, + session, + interaction, this_game=this_game, lineup_team=this_team, sheets_auth=self.sheets, lineup_num=int(lineup.value), league_name=this_game.game_type ) + except PositionNotFoundException as e: + logger.error(f'Position validation failed during lineup load: {e}') + await interaction.edit_original_response(content=str(e)) + return except LineupsMissingException as e: await interaction.edit_original_response(content='Run `/set starting-pitcher` to select your SP') return diff --git a/command_logic/logic_gameplay.py b/command_logic/logic_gameplay.py index dd763e4..b6a2ee0 100644 --- a/command_logic/logic_gameplay.py +++ b/command_logic/logic_gameplay.py @@ -628,7 +628,13 @@ async def read_lineup( for batter in human_lineups: if batter.position != "DH": - await get_position(session, batter.card, batter.position) + try: + await get_position(session, batter.card, batter.position) + except PositionNotFoundException: + raise PositionNotFoundException( + f"Could not find {batter.position} ratings for **{batter.player.name_with_desc}**. " + f"Please check your lineup in Google Sheets and make sure each player is at a valid position." + ) return this_game.initialize_play(session) @@ -1096,6 +1102,19 @@ async def get_lineups_from_sheets( raise SyntaxError( f"Easy there, champ. Looks like card ID {row[1]} belongs to the {this_card.team.lname}. Try again with only cards you own." ) + + position = row[0].upper() + if position != "DH": + player_positions = [ + getattr(this_card.player, f"pos_{i}") for i in range(1, 9) + if getattr(this_card.player, f"pos_{i}") is not None + ] + if position not in player_positions: + raise PositionNotFoundException( + f"**{this_card.player.name_with_desc}** (card {this_card.id}) is listed at **{position}** in your lineup, " + f"but can only play {', '.join(player_positions)}. Please fix your lineup in Google Sheets." + ) + card_id = row[1] card_ids.append(str(card_id)) -- 2.25.1 From a79bfbe6eba0d05d1cdbf017ccb6d558ce9bbdd8 Mon Sep 17 00:00:00 2001 From: cal Date: Sun, 8 Feb 2026 03:03:50 +0000 Subject: [PATCH 2/2] Update VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index bfa363e..8decb92 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.8.4 +1.8.5 -- 2.25.1