discord.js: GuildChannel#permissionsLocked incorrect when parent category updated

Please describe the problem you are having in as much detail as possible:

Assume a situation where a text channel is in a category, with the channel’s permissions set in Discord to sync with the category.

Accessing GuildChannel#permissionsLocked for that channel will confirm this.

Now, assume that the permissions overwrites in the category are updated, and you have an event listener set for channel updates.

First, you will see the update come through for the category permissions overwrites being updated, which is all fine (or is it).

Then, you will see the update come through for the channel’s permissions overwrites being updated. At this point though, if you access GuildChannel#permissionsLocked on the old channel, it will report that the permissions were not locked, even though they were, and GuildChannel#permissionsLocked on the new channel will report that the permissions are not locked.

This happens (from my understanding) because we’ve already received the update event for the category overwrites changing, so both the old and new channel in the channel update event have the same, already updated, category as their parent which is used for the GuildChannel#permissionsLocked calculation.

Include a reproducible code sample here, if possible:

client.on('channelUpdate', (oldChannel, newChannel) => {
    const permsUpdated = newChannel.permissionOverwrites.size !== oldChannel.permissionOverwrites.size
        || newChannel.permissionOverwrites.some(overwrite => !oldChannel.permissionOverwrites.has(overwrite.id)
            || !oldChannel.permissionOverwrites.get(overwrite.id).allow.equals(overwrite.allow)
            || !oldChannel.permissionOverwrites.get(overwrite.id).deny.equals(overwrite.deny));

    if (!permsUpdated) return;

    if (newChannel.type === 'category') {
        console.log('Category permissions updated');
        return;
    }

    if (newChannel.type === 'text') {
        console.log('Text channel permissions updated');
        console.log('Synced before update:', oldChannel.permissionsLocked);
        console.log('Synced after update:', newChannel.permissionsLocked);
        return;
    }
});

Further details:

  • discord.js version: 13.0.0 (dev)
  • Node.js version: 14.15.4
  • Operating system: macOS
  • Priority this issue should have – please be realistic and elaborate if possible: P2 – its an annoying issue that I’d love to see a fix or workaround for, but also somewhat minor

Relevant client options:

  • partials: channel (though both GuildChannel and Channel do not have a partial prop)

  • gateway intents: guilds

  • other: none

  • I have also tested the issue on latest master, commit hash: fe6cc0c15dde99caa1049d35f75b9335ace1721d

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Comments: 27 (15 by maintainers)

Most upvoted comments

permissionsLocked here isn’t a clear value though - the API doesn’t provide a boolean telling us that a channel is synced or not. It’s a getter that compares the channel’s overwrites to the parent’s overwrites at runtime. Because it’s a runtime check, there’s several reasons this simply can’t be accurate.

oldChildChannel.permissionsLocked would be comparing to the current, updated parent channels overwrites. I don’t think its even possible to freeze the parent’s old state for this, or somehow freeze the value of this getter, since as you said, the event for the update on the category is already processed before the children emit.

The closest thing I can think of to a solution would be some mess where when the Category is updated it checks all its children, sees if they’re currently synced, and creates some sort of oldParent, oldParentOverwrites or oldPermissionsLocked on those channels before doing its own update, purely so that value could be used when the child event emits.

Side note - since this isn’t a proper API-provided boolean doesnt this mean it can return a false positive if a parent and child have the same overwrites without being in sync?

I recommend creating a PR though so that proposed changes to solve this issue can be discussed there, directly on your proposed changes, rather than in the issue 😃

To put it in simple terms, no, this doesn’t sound like it solves the issue. It sounds like you’re removing the concept of permissions overwrites on channels, which makes no sense at all.

Yup, that sounds like the right theory.

No, it does not defeat the purpose… the whole idea here is that I want to be able to accurately report when a channel has its permissions updated in such a way that means the permissions are no longer set to sync, or have been updated when they were previously not in sync and are now in sync.

This current behaviour results in continual false positives, as the value of permissionsLocked switches indicating that the sync of the channel permissions was updated, when in reality the option to sync wasn’t updated, the permissions were in fact updating to remain in syc.

Yes 👍, because they were still set to be synced, and were updated in exactly the same way the parent was, as they are synced.

Yes, based on the current logic, it is the expected behaviour. However, given that permissionsLocked is a helper provided by D.js rather than by Discord, it would be nice for it to be improved, so that it can handle this situation and correctly reflect that the permissions were set to be in sync the whole time.

Yup, as I noticed two replies above, with recent changes to master I believe it is now permissionOverwrites.cache.some

No, the parent is updated first. If you run my repro code locally you will be able to observe the parent update event arrives first, which updates the d.js cache for both the parent and therefore the child. The child update event then arrives after, at which point the parent has already been updated and so the child is perceived as out of sync.

in the child category the new and old channel gives the same out when called for permissionsLocked.

That is one consequence of the issue, yes, but it is not the root cause. The root cause is that the parent receives an update first, so when the child update comes through the child is perceived to be out of sync with the parent, when in reality the child is being updated because it is in sync with the parent.

Also, what is permissionOverwrites.some?

https://discord.js.org/#/docs/collection/master/class/Collection?scrollTo=some

(This is now actually permissionOverwrites.cache.some per recent changes iirc)

Nope, this still happens as of f72ce7c136cf2dfe31a67b190c00e30ba7d70bfa. I believe this is just how Discord sends the permissions updates for the parent channel and the children, but it would be nice if d.js handled it better.