Skip to content

Fix/invite handlers null guild#11475

Open
GreenHolland wants to merge 2 commits intodiscordjs:mainfrom
GreenHolland:fix/invite-handlers-null-guild
Open

Fix/invite handlers null guild#11475
GreenHolland wants to merge 2 commits intodiscordjs:mainfrom
GreenHolland:fix/invite-handlers-null-guild

Conversation

@GreenHolland
Copy link
Copy Markdown

INVITE_CREATE and INVITE_DELETE gateway handlers crash with TypeError: Cannot read properties of undefined (reading 'invites') when the guild is not in cache.

Other handlers like GUILD_BAN_ADD correctly guard with if (!guild) return;.

`resolveBody()` crashes with `TypeError: Cannot read properties of
undefined (reading 'push')` when `options.attachments` is provided
without `options.files`, because `files?.map()` returns `undefined`
and `.push()` is then called on it.

Default the result to an empty array with `?? []`.
INVITE_CREATE and INVITE_DELETE handlers crash with `TypeError: Cannot
read properties of undefined (reading 'invites')` when the guild is not
in cache, unlike other handlers (e.g. GUILD_BAN_ADD) which correctly
guard with `if (!guild) return;`.
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
discord-js Skipped Skipped Apr 3, 2026 10:27am
discord-js-guide Skipped Skipped Apr 3, 2026 10:27am

Request Review

@vercel vercel bot temporarily deployed to Preview – discord-js April 3, 2026 10:27 Inactive
@vercel vercel bot temporarily deployed to Preview – discord-js-guide April 3, 2026 10:27 Inactive
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

📝 Walkthrough

Walkthrough

Three defensive programming fixes across the discord.js library: guard conditions added to INVITE_CREATE and INVITE_DELETE websocket handlers to check guild existence, and initialization logic in MessagePayload adjusted to ensure attachments array is always defined.

Changes

Cohort / File(s) Summary
Websocket Invite Handlers
packages/discord.js/src/client/websocket/handlers/INVITE_CREATE.js, packages/discord.js/src/client/websocket/handlers/INVITE_DELETE.js
Added guild existence checks to early-return conditions. Both handlers now verify both channel and guild are present before proceeding with invite operations, preventing undefined guild reference errors.
Message Payload Initialization
packages/discord.js/src/structures/MessagePayload.js
Changed attachments initialization from undefined to empty array when this.options.files is absent, ensuring consistent payload structure and allowing subsequent attachment operations on a defined array.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly addresses the main fix: adding null guild checks to invite handlers to prevent crashes.
Description check ✅ Passed The description clearly explains the issue (crashes in INVITE_CREATE/DELETE when guild is missing) and references how other handlers guard against this.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/discord.js/src/structures/MessagePayload.js`:
- Around line 193-200: The attachments mapping in MessagePayload (the
this.options.files?.map block) builds attachment objects without using
file.toJSON(), so custom IDs set via AttachmentBuilder.setId() are ignored;
change the mapper to call file.toJSON() when available and spread its result
into the attachment object after the index-based id (e.g., create object with
id: index.toString() and then ...file.toJSON() or ...file.toJSON?.()) so any
custom id from AttachmentBuilder overrides the default index id while still
falling back to existing fields when toJSON() is not present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: db093f59-2266-4cbe-8bcf-17eb29a379a8

📥 Commits

Reviewing files that changed from the base of the PR and between 1f9affd and c2220ed.

📒 Files selected for processing (3)
  • packages/discord.js/src/client/websocket/handlers/INVITE_CREATE.js
  • packages/discord.js/src/client/websocket/handlers/INVITE_DELETE.js
  • packages/discord.js/src/structures/MessagePayload.js
📜 Review details
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: Qjuh
Repo: discordjs/discord.js PR: 11423
File: packages/discord.js/src/structures/MessagePayload.js:193-206
Timestamp: 2026-02-16T18:45:40.408Z
Learning: In discord.js MessagePayload.js, when mapping files to attachments, custom ids set via AttachmentBuilder.setId() should override the index-based id. The spread operator should come after the explicit id assignment to allow toJSON() values to take precedence.
📚 Learning: 2026-02-16T18:45:40.408Z
Learnt from: Qjuh
Repo: discordjs/discord.js PR: 11423
File: packages/discord.js/src/structures/MessagePayload.js:193-206
Timestamp: 2026-02-16T18:45:40.408Z
Learning: In discord.js MessagePayload.js, when mapping files to attachments, custom ids set via AttachmentBuilder.setId() should override the index-based id. The spread operator should come after the explicit id assignment to allow toJSON() values to take precedence.

Applied to files:

  • packages/discord.js/src/structures/MessagePayload.js
📚 Learning: 2026-03-30T11:04:39.419Z
Learnt from: almeidx
Repo: discordjs/discord.js PR: 11471
File: packages/discord.js/src/util/Util.js:83-86
Timestamp: 2026-03-30T11:04:39.419Z
Learning: For the discord.js and rest packages, follow the existing convention for constructing request URLs: use the `api` base URL and `version` string via raw interpolation (e.g., `${api}/v${version}${route}`) without adding normalization such as trimming trailing slashes on `api` or stripping/adding/remapping a leading `v` on `version`. Do not recommend changing this behavior during code review unless the existing pattern in this repo is intentionally being replaced.

Applied to files:

  • packages/discord.js/src/structures/MessagePayload.js
  • packages/discord.js/src/client/websocket/handlers/INVITE_CREATE.js
  • packages/discord.js/src/client/websocket/handlers/INVITE_DELETE.js
📚 Learning: 2026-03-10T13:07:50.826Z
Learnt from: Qjuh
Repo: discordjs/discord.js PR: 11443
File: packages/discord.js/src/structures/DMChannel.js:83-94
Timestamp: 2026-03-10T13:07:50.826Z
Learning: In discord.js (packages/discord.js/src/structures/DMChannel.js), `DMChannel#recipientId` intentionally returns `null` for DMChannels that do not include the client user (e.g. DMs between two non-client users observed via user-installed apps). The methods `fetch()` and `toString()` use `recipientId` unconditionally; calling them on a non-client DMChannel is considered a caller error and is intentional API design — not a bug to guard against.

Applied to files:

  • packages/discord.js/src/client/websocket/handlers/INVITE_CREATE.js
  • packages/discord.js/src/client/websocket/handlers/INVITE_DELETE.js
🔇 Additional comments (3)
packages/discord.js/src/client/websocket/handlers/INVITE_DELETE.js (1)

9-9: LGTM! Proper defensive guard to prevent crash.

The combined check for both channel and guild before proceeding is correct. Line 14 accesses guild.invites.cache.delete() which would throw if guild is undefined. This follows the established pattern used in other handlers like VOICE_STATE_UPDATE and THREAD_LIST_SYNC.

packages/discord.js/src/client/websocket/handlers/INVITE_CREATE.js (1)

8-8: LGTM! Consistent with the INVITE_DELETE fix.

The guard ensures guild.invites._add() on line 11 won't throw when the guild isn't cached. This mirrors the fix in INVITE_DELETE.js and aligns with the pattern in other handlers like THREAD_LIST_SYNC.js and VOICE_STATE_UPDATE.js.

packages/discord.js/src/structures/MessagePayload.js (1)

193-200: Fix correctly prevents the crash but introduces an unintended behavioral change for message edits.

The ?? [] fallback properly fixes the crash when options.attachments is provided without options.files. However, this changes the payload from omitting attachments to including attachments: [] when neither files nor attachments options are provided.

Per Discord API semantics documented in the codebase (MessageManager.js:217-218: "All attachments will be kept if omitted"), an empty attachments array signals "remove all attachments," whereas omitting the field preserves existing attachments. This can unintentionally strip attachments from messages when users edit only content or embeds.

Verify whether the empty array behavior is intentional, or whether attachments should be conditionally included only when explicitly set or non-empty.

Comment on lines +193 to +200
const attachments =
this.options.files?.map((file, index) => ({
id: index.toString(),
description: file.description,
title: file.title,
waveform: file.waveform,
duration_secs: file.duration,
})) ?? [];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Custom file IDs from AttachmentBuilder.setId() may not propagate to attachment metadata.

The mapping extracts fields explicitly without calling toJSON() on files, so custom IDs set via AttachmentBuilder.setId() won't override the index-based id. Based on learnings, custom IDs should take precedence over index-based assignment.

If files can be AttachmentBuilder instances with custom IDs, consider:

Allow toJSON() values to override explicit fields
 const attachments =
   this.options.files?.map((file, index) => ({
     id: index.toString(),
     description: file.description,
     title: file.title,
     waveform: file.waveform,
     duration_secs: file.duration,
+    ...(isJSONEncodable(file) ? file.toJSON() : {}),
   })) ?? [];

Based on learnings: "In discord.js MessagePayload.js, when mapping files to attachments, custom ids set via AttachmentBuilder.setId() should override the index-based id. The spread operator should come after the explicit id assignment to allow toJSON() values to take precedence."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/discord.js/src/structures/MessagePayload.js` around lines 193 - 200,
The attachments mapping in MessagePayload (the this.options.files?.map block)
builds attachment objects without using file.toJSON(), so custom IDs set via
AttachmentBuilder.setId() are ignored; change the mapper to call file.toJSON()
when available and spread its result into the attachment object after the
index-based id (e.g., create object with id: index.toString() and then
...file.toJSON() or ...file.toJSON?.()) so any custom id from AttachmentBuilder
overrides the default index id while still falling back to existing fields when
toJSON() is not present.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The changes to this file should be reverted

@github-project-automation github-project-automation bot moved this from Todo to Review in Progress in discord.js Apr 3, 2026
@Qjuh
Copy link
Copy Markdown
Member

Qjuh commented Apr 3, 2026

Did you actually test this and received that error? Or are you assuming it would? Because there should be no way to have a guild channel in cache when the Guild itself isn't cached. So the if (!channel) return false; should already assure the Guild is cached.

@GreenHolland
Copy link
Copy Markdown
Author

No I am sorry I should have, I didn't test it. I started reading the repo and tried to find a few things that need fixing or help out, as I started using it. Will make sure to create a dummy app and test it in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Review in Progress

Development

Successfully merging this pull request may close these issues.

3 participants