NullReferenceException in DiscordChannel.PermissionsFor(DiscordMember)
Created by: ParanormalVibe
Summary
A NullReferenceException
has a chance to occur when calling DiscordChannel.PermissionsFor(DiscordMember)
and passing an unmodified DiscordMember
supplied by the MessageCreated
event.
Details
This issue was first reported by @xSke in the DSharpPlus #support channel. The exception occurs due to at least one of the roles xr
in the Linq expression below being null.
https://github.com/DSharpPlus/DSharpPlus/blob/f65d2e6100e37e1617381c9d9a4696e080f1d98d/DSharpPlus/Entities/DiscordChannel.cs#L608
The bot encountering this exception is currently using DSharpPlus version 4.0.0-nightly-00686, and targets .NET Core 3.1.
Stack Trace (from #support channel):
System.NullReferenceException: Object reference not set to an instance of an object.
at DSharpPlus.Entities.DiscordChannel.<>c__DisplayClass89_0.<PermissionsFor>b__0(DiscordRole xr)
at System.Linq.Enumerable.WhereEnumerableIterator`1.ToArray()
at DSharpPlus.Entities.DiscordChannel.PermissionsFor(DiscordMember mbr)
at PluralKit.Bot.DiscordUtils.PermissionsInGuild(DiscordChannel channel, DiscordMember member) in /srv/pluralkit/PluralKit.Bot/Utils/DiscordUtils.cs:line 35
at PluralKit.Bot.DiscordUtils.PermissionsIn(DiscordChannel channel, DiscordUser user) in /srv/pluralkit/PluralKit.Bot/Utils/DiscordUtils.cs:line 45
at PluralKit.Bot.ProxyService.SanitizeEveryoneMaybe(DiscordMessage message, String messageContents) in /srv/pluralkit/PluralKit.Bot/Services/ProxyService.cs:line 220
at PluralKit.Bot.ProxyService.HandleMessageAsync(DiscordClient client, GuildConfig guild, CachedAccount account, DiscordMessage message, Boolean doAutoProxy) in /srv/pluralkit/PluralKit.Bot/Services/ProxyService.cs:line 165
at PluralKit.Bot.MessageCreated.TryHandleProxy(MessageCreateEventArgs evt, GuildConfig cachedGuild, CachedAccount cachedAccount) in /srv/pluralkit/PluralKit.Bot/Handlers/MessageCreated.cs:line 125
at PluralKit.Bot.MessageCreated.Handle(MessageCreateEventArgs evt) in /srv/pluralkit/PluralKit.Bot/Handlers/MessageCreated.cs:line 71
at PluralKit.Bot.Bot.<>c__DisplayClass8_0`1.<<HandleEvent>g__HandleEventInner|0>d.MoveNext() in /srv/pluralkit/PluralKit.Bot/Bot.cs:line 90
Steps to reproduce
I haven't found a way to reliably reproduce this issue, and it only seems to occur in a certain server with certain members. It is possible to deliberately trigger the same exception externally by using reflection:
private void CauseException(DiscordMember member)
{
var roleIdCache = GetPrivateField();
roleIdCache.Add(12345); // add a role to the member's role cache that the guild role cache doesn't have
member.Roles.All(x => x.Id == 0); // an exception occurs due to member.Roles having null entries
}
private List<ulong> GetPrivateField()
{
var roleIdsField = typeof(DiscordMember).GetField("_role_ids", BindingFlags.NonPublic | BindingFlags.Instance);
return roleIdsField.GetValue(member) as List<ulong>;
}
Notes
I think that the cached DiscordMember
's roles are falling out of sync with those of the cached DiscordGuild
. I'm not sure why -- maybe Discord failed to send the gateway event for Member Update
?
I created a temporary workaround for the bot affected by this using reflection in https://github.com/xSke/PluralKit/pull/169, and I'm submitting a PR with a workaround for this. I felt that it was appropriate to open an issue since I'm not sure what's actually causing the member cache to become like this.