discordjs/discord.js · error · DiscordjsError

GuildChannelOrphan

GuildChannelOrphan

Error message

GuildChannelOrphan

What it means

GuildChannelOrphan is thrown by GuildChannel#lockPermissions when the channel has no parent — there is no parent channel from which to copy permission overwrites. 'Orphan' means the channel is not attached to a category (or the parent is uncached/unresolvable), so locking permissions from a parent is impossible.

Source

Thrown at packages/discord.js/src/structures/GuildChannel.js:286

    const basePermissions = new PermissionsBitField([role.permissions, role.guild.roles.everyone.permissions]);
    const everyoneOverwrites = this.permissionOverwrites.cache.get(this.guild.id);
    const roleOverwrites = this.permissionOverwrites.cache.get(role.id);

    return basePermissions
      .remove(everyoneOverwrites?.deny ?? PermissionsBitField.DefaultBit)
      .add(everyoneOverwrites?.allow ?? PermissionsBitField.DefaultBit)
      .remove(roleOverwrites?.deny ?? PermissionsBitField.DefaultBit)
      .add(roleOverwrites?.allow ?? PermissionsBitField.DefaultBit)
      .freeze();
  }

  /**
   * Locks in the permission overwrites from the parent channel.
   *
   * @returns {Promise<GuildChannel>}
   */
  async lockPermissions() {
    if (!this.parent) throw new DiscordjsError(ErrorCodes.GuildChannelOrphan);
    const permissionOverwrites = this.parent.permissionOverwrites.cache.map(overwrite => overwrite.toJSON());
    return this.edit({ permissionOverwrites });
  }

  /**
   * A collection of cached members of this channel, mapped by their ids.
   * Members that can view this channel, if the channel is text-based.
   * Members in the channel, if the channel is voice-based.
   *
   * @type {Collection<Snowflake, GuildMember>}
   * @readonly
   */
  get members() {
    return this.guild.members.cache.filter(member =>
      this.permissionsFor(member).has(PermissionFlagsBits.ViewChannel, false),
    );
  }

View on GitHub (pinned to a81ed8a306)

Solutions

  1. Check channel.parent before calling: `if (!channel.parent) return;` and skip or create a category first
  2. Assign the channel to a category (channel.setParent(categoryId, { lockPermissions: true })) instead of manually locking
  3. If the parent may be uncached, fetch it: `const parent = await channel.guild.channels.fetch(channel.parentId)` and copy its permissionOverwrites manually via channel.edit({ permissionOverwrites: ... })
  4. Guard in bulk-sync loops: only call lockPermissions for channels where parentId is set

Example fix

// before
await channel.lockPermissions(); // throws when channel has no category
// after
if (!channel.parent) return;
await channel.lockPermissions();
Defensive patterns

Strategy: validation

Validate before calling

if (!channel.parent) return; // orphan: nothing to lock from
await channel.lockPermissions();

Type guard

function hasParentChannel(channel) {
  return channel.parent != null || typeof channel.parentId === 'string';
}

Try / catch

try {
  await channel.lockPermissions();
} catch (err) {
  if (err.name === 'DiscordjsError' && err.message.includes('GuildChannelOrphan')) {
    return; // channel is not in a category; skip
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling channel.lockPermissions() on a channel whose parent is null — a top-level channel not inside any category, or a channel whose parent exists on Discord's side but isn't cached/resolved by the client yet.

Common situations: Syncing permissions of a channel programmatically when the channel was moved out of its category; running lockPermissions in setup scripts before the parent channel is fetched/cached; assuming every channel has a parent; comparing with the Discord UI 'Sync Permissions' which is disabled for non-category children.

Related errors


AI-assisted analysis of discordjs/discord.js@a81ed8a306 (2026-08-30). Data as JSON: /api/errors/1320a7926507aa3f. Report an issue: GitHub.