Skip to content

security: add catch block to async game.end() call (SEC-05)#3948

Open
berkelmali wants to merge 2 commits into
openfrontio:mainfrom
berkelmali:fix/sec05-game-end-promise
Open

security: add catch block to async game.end() call (SEC-05)#3948
berkelmali wants to merge 2 commits into
openfrontio:mainfrom
berkelmali:fix/sec05-game-end-promise

Conversation

@berkelmali
Copy link
Copy Markdown
Contributor

@berkelmali berkelmali commented May 16, 2026

Description: In TransportShipExecution.ts, a call to game.end() was not awaited and lacked a .catch() block. This could lead to unhandled promise rejections if the game end logic fails. Fix: Added a .catch(e => logger.error(e)) block to the game.end() call.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

barfires

game.end() is async but was called without await inside a try/catch.
The try/catch only catches synchronous errors. Added .catch() to the
returned promise to prevent unhandled promise rejections from crashing
the server.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

Review Change Stack

Walkthrough

The GameManager.tick() method's GamePhase.Finished handling replaces a synchronous game.end() call with an asynchronous game.end().catch(...) pattern. Promise rejections from the end operation are now explicitly logged via this.log.error instead of being captured by the surrounding try/catch block.

Changes

Game End Error Handling

Layer / File(s) Summary
Promise rejection handling in game end
src/server/GameManager.ts
The game.end() call is invoked as a promise and .catch(...) is attached to log rejections via this.log.error, replacing the previous synchronous try/catch handling.

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested reviewers

  • evanpelle

Poem

A promise once lost in the catch,
Now speaks up with logs that attach,
When games reach their end,
Errors will send
A message—no silence to patch. 🎮

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description mentions fixing an issue in TransportShipExecution.ts, but the actual changes are in GameManager.ts. This is a significant mismatch between the described and actual files being modified. Clarify whether the changes are in TransportShipExecution.ts or GameManager.ts, and update the description to accurately reflect the actual files modified in this pull request.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: adding error handling to an async game.end() call, which aligns with the changeset modifications in GameManager.ts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/server/GameManager.ts (1)

133-139: ⚡ Quick win

The try-catch block no longer serves a purpose.

Since game.end() returns a promise and is not awaited, the try-catch block cannot catch errors from the async operation. Promise rejections are only handled by the .catch() handler on line 134.

If fire-and-forget is the intended pattern, remove the try-catch block for clarity:

🧹 Simplified code without redundant try-catch
-      if (phase === GamePhase.Finished) {
-        try {
-          game.end().catch((error: any) => {
-            this.log.error(`error ending game ${id}: ${error}`);
-          });
-        } catch (error) {
-          this.log.error(`error ending game ${id}: ${error}`);
-        }
-      } else {
+      if (phase === GamePhase.Finished) {
+        game.end().catch((error: any) => {
+          this.log.error(`error ending game ${id}: ${error}`);
+        });
+      } else {

If cleanup must complete before removing the game from the manager, await the promise instead:

⏳ Alternative: await cleanup before removing game
-      if (phase === GamePhase.Finished) {
-        try {
-          game.end().catch((error: any) => {
-            this.log.error(`error ending game ${id}: ${error}`);
-          });
-        } catch (error) {
-          this.log.error(`error ending game ${id}: ${error}`);
-        }
-      } else {
+      if (phase === GamePhase.Finished) {
+        try {
+          await game.end();
+        } catch (error) {
+          this.log.error(`error ending game ${id}: ${error}`);
+        }
+      } else {

Note: This would require marking tick() as async.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/GameManager.ts` around lines 133 - 139, Remove the redundant
try-catch around the fire-and-forget call to game.end(): keep the promise and
its .catch(...) handler on game.end() and delete the outer try { ... } catch {
... } block in GameManager.tick (or wherever this appears) so promise rejections
are handled only by the existing .catch; if instead you require synchronous
cleanup, change tick to async and replace game.end().catch(...) with await
game.end() inside a try/catch to properly await and handle errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/server/GameManager.ts`:
- Around line 133-139: Remove the redundant try-catch around the fire-and-forget
call to game.end(): keep the promise and its .catch(...) handler on game.end()
and delete the outer try { ... } catch { ... } block in GameManager.tick (or
wherever this appears) so promise rejections are handled only by the existing
.catch; if instead you require synchronous cleanup, change tick to async and
replace game.end().catch(...) with await game.end() inside a try/catch to
properly await and handle errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4f7cd55b-e6ca-4cc8-aa97-8ccb177bb136

📥 Commits

Reviewing files that changed from the base of the PR and between b813792 and 5320bb6.

📒 Files selected for processing (1)
  • src/server/GameManager.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

1 participant