Discord Channel Obfuscation: Bots and ___hidden___ Channels

Published on

From 16 November 2026 Discord hides channels your bot cannot view: the Gateway names them ___hidden___ and the channel list drops them. How to prepare.

Cartoon bot mascot sitting on a small server box with online status lights

From Monday 16 November 2026, Discord stops giving bots the details of channels they cannot view: GET /guilds/{guild.id}/channels leaves those channels out, and the Gateway still sends them, but with the name ___hidden___, sensitive fields stripped and a new CHANNEL_OBFUSCATED flag. If your bot lists, audits, backs up or logs channels, it will quietly see fewer channels or placeholder ones from that day, so make it check the flag and make sure it has View Channel wherever it really needs to look.

Discord announced the change on 12 August 2026 in its developer change log, tagged as a breaking change for the Gateway and the HTTP API, and says it "becomes mandatory for all bots on November 16, 2026." None of this shows up as an error. A backup just comes out smaller, a log line mentions #___hidden___, and a permission report shows a lone @everyone deny instead of the real role overwrites. Below is what the docs say, what they leave open, how to test today and what to change in your code.

What changes on 16 November

WhereUntil 16 NovemberFrom 16 November 2026
Gateway: Guild Create, Channel Create, Channel UpdateFull data for every channel, including ones the bot cannot viewChannels the bot cannot view still arrive, named ___hidden___, with the CHANNEL_OBFUSCATED flag and other sensitive fields "nulled or reduced"
GET /guilds/{guild.id}/channelsEvery channel in the guildChannels the bot cannot view are left out of the response
Interaction payloadsFull channel dataNo change, full channel data

Discord gives its reason plainly: "Channels a user doesn't have access to shouldn't be accessible or visible to that user. This change is happening to respect the privacy of users. Because it's happening for users this means it will also apply to bots." Regular users will soon stop seeing channels they have no access to, and bots get the same rule.

Two things the docs do not give you. There is no time of day, only the date. And there is no opt-out: neither the change log nor the API reference mentions a way to keep the old behaviour, and the Gateway docs say obfuscation is "planned to apply to all bots automatically, even when they don't provide this capability." For the wider list of API changes to watch this year, see Discord API changes in 2026.

Which bots are affected

Discord's change log names bots that "mirror or audit a server's full channel list, calculate permissions across every channel, or report on channel structure regardless of the bot's own access." It also says: "If your bot only acts in channels it has permission to view, this change does not affect you."

In practice, check these first:

  • Backup and server-clone bots that copy channels, topics and permission overwrites.
  • Audit and permission-report bots that show who can see which channel.
  • Logging bots that post channel creates, renames and permission changes.
  • Stats bots, web dashboards and setup wizards that count channels or build a channel picker from the REST list.

A bot with the Administrator permission will not notice anything. Discord's permissions table says Administrator "bypasses channel permission overwrites", so such a bot can view every channel. The bots that lose data are the carefully configured ones that only got the permissions they needed.

Endpoints and events, according to the docs

This is what Discord documents:

  • HTTP. Get Guild Channels, GET /guilds/{guild.id}/channels, "will omit any channel your bot doesn't have permissions to view". It already "Does not include threads." The HTTP API never sets the CHANNEL_OBFUSCATED flag: hidden channels are simply absent.
  • Gateway. Obfuscated channels "are dispatched through the normal Channel Create and Channel Update gateway events, including within the channel list of a Guild Create event." Those events belong to the GUILDS intent, so this is not an intent change. Our intents and Gateway guide covers how intents work.
  • Regaining access. When the bot can see a channel again (a permission overwrite change, a change to its roles, a change to a role's permissions, or joining a voice channel it could not see), the Gateway sends a Channel Update with the full channel. If the parent category was hidden as well, the category gets its own Channel Update.
  • Interactions. They "are built through a separate path that does not apply obfuscation, so an app always receives full channel data in interaction payloads for channels it can act on."

"Can view" has a precise meaning in the channel docs: VIEW_CHANNEL on the channel, or being connected to the voice channel. A category counts as viewable if any channel inside it is viewable.

What the docs do not say, as of 16 October 2026:

  • What the Gateway sends at the moment a bot loses access to a channel it could see. Only the way back is described.
  • Whether Channel Delete fires for channels the bot cannot view.
  • Anything about thread events or thread endpoints. Guild Create's threads list already only contains "active threads in the guild that current user has permission to view".
  • Anything about GET /channels/{channel.id}, audit logs, invites or webhooks in connection with this change.
  • Exactly which fields are "nulled or reduced".
  • Anything about API versions.

What ___hidden___ means

___hidden___ is the placeholder name Discord puts on a channel your bot cannot view. Only a few fields on such a channel can be trusted:

FieldOn an obfuscated channel
id, type, position, parent_idReal. Discord says these "will not be obfuscated"
name"___hidden___"
flagsCHANNEL_OBFUSCATED is set: 1 << 17, which is 131072
permission_overwritesA single entry denying VIEW_CHANNEL for the @everyone role
Everything else"nulled or reduced", and "should not be relied on"

Detect hidden channels with the flag, never with the name. Discord says so directly: "Do not rely on inspecting name or any other obfuscated field." The overwrite is a placeholder as well, so it tells you nothing about which roles can really see the channel.

How to test your bot before 16 November

Discord offers two switches for the Gateway side, both "temporary and for testing":

  1. Developer Portal toggle. Open your application in the Developer Portal, go to Overview > Bot and enable Private Channel Obfuscation. No code change, any library. Discord does not say whether a running session picks it up, so restart the bot after flipping it.
  2. Identify capability. Send 1 << 15 (32768) in the capabilities field of your Identify payload. Discord calls this CHANNEL_OBFUSCATION and warns that "This opt-in mechanism will change before the feature reaches general availability." Few libraries can send it today: the stable discord.js 14.27.0 does not (the option only exists on the development branch of @discordjs/ws), and the Identify code of discord.py and Discord.Net has no such field.

capabilities is a separate Identify field from intents. The MESSAGE_CONTENT intent happens to be 1 << 15 as well, so sending that intent does not turn on the test.

There is no early opt-in for HTTP. From 16 November, GET /guilds/{guild.id}/channels simply returns the channels your bot can view.

A short test run:

  1. Use a separate test application and test server if you have them. The toggle is an application setting, so try it there before you touch your production application.
  2. Create a text channel, deny View Channel for @everyone, and do not give the bot access.
  3. Enable the toggle, restart the bot and run every feature that lists, audits, backs up or logs channels. Look for ___hidden___ in the output and for channels that vanish.
  4. Give the bot View Channel on the test channel. A Channel Update with the real name should arrive. Remove the access again and see what your library does, because Discord does not document that direction.
  5. For code that calls GET /guilds/{guild.id}/channels, filter the response yourself to the channels the bot can view and check that nothing breaks on the shorter list.

Code changes to make now

Detect obfuscated channels by the flag

discord.js gives every channel a flags bitfield. The named constant ChannelFlags.ChannelObfuscated arrived in discord-api-types 0.38.54 on 28 August 2026, and discord.js 14.27.0 depends on ^0.38.49, so a fresh install or npm update picks it up. The raw bit works with any version:

const { ChannelFlags } = require('discord.js');

// Named in discord-api-types 0.38.54 and newer, the raw bit works everywhere.
const OBFUSCATED = ChannelFlags.ChannelObfuscated ?? (1 << 17);

function isObfuscated(channel) {
  return ((channel.flags?.bitfield ?? 0) & OBFUSCATED) !== 0;
}

function channelReport(guild) {
  const visible = [];
  const hidden = [];
  for (const channel of guild.channels.cache.values()) {
    if (isObfuscated(channel)) {
      // Only id, type, position and parent_id are real on this object.
      hidden.push({ id: channel.id, type: channel.type, parentId: channel.parentId });
    } else {
      visible.push({ id: channel.id, name: channel.name });
    }
  }
  return { visible, hidden };
}

In JDA 6.7.0 the check is channel.isObfuscated(). discord.py and Discord.Net have no named flag yet (see the library table further down), so use the permission check from the next section there. In discord.py, text, voice and category channels do not even expose flags; only forum channels and threads do.

Check View Channel before you read a channel

An obfuscated channel carries a single @everyone deny for View Channel, so the permission calculation in discord.js, discord.py and Discord.Net comes out as "cannot view" for it. That makes the plain permission check a decent detector, and you want it anyway before you read messages:

import discord

def bot_can_view(channel: discord.abc.GuildChannel) -> bool:
    # False for an obfuscated channel: its only overwrite denies View Channel for @everyone.
    return channel.permissions_for(channel.guild.me).view_channel

async def export_history(channel: discord.TextChannel):
    perms = channel.permissions_for(channel.guild.me)
    if not (perms.view_channel and perms.read_message_history):
        return None  # store channel.id as "skipped, no access" instead
    return [message async for message in channel.history(limit=100)]

In discord.js the same check is channel.viewable, and in Discord.Net it is guild.CurrentUser.GetPermissions(channel).ViewChannel.

One catch: Discord counts a category as viewable when any channel inside it is viewable, so it sends that category with real data even if the bot has no View Channel on the category itself. A pure permission check marks such a category as hidden. Where your library exposes the flag, trust the flag for categories.

Missing is not deleted

From 16 November the REST list and the Gateway cache disagree. In discord.js, guild.channels.fetch() without an ID calls GET /guilds/{guild.id}/channels and returns only what came back. It does not remove anything from guild.channels.cache, which still holds the obfuscated copies from the Gateway. In discord.py, Guild.fetch_channels() calls the same endpoint.

So:

  • Never treat "not in the REST list" as "deleted". If you sync a database of channels, or settings such as a log channel or ticket category, against that list, mark missing channels as not visible to the bot and keep the ID and the settings.
  • Do not overwrite stored names, topics or overwrites with data from a channel that has the flag. Keep the last good copy and mark it hidden.
  • When a later Channel Update arrives without the flag, it carries full data again. Refresh from that.
  • Tell the user. Discord points bots that manage channels to its docs on how to "detect channels your bot can't see and surface that to users". A dashboard line such as "2 channels are hidden from the bot" saves you a support ticket.

Logging bots

A logging bot gets Channel Create events for new private channels it cannot view, with the placeholder name. Log those as hidden channels instead of printing #___hidden___, and do not log the switch to the placeholder as a rename or a permission change. A discord.py version, where log() stands for whatever posts to your log channel:

@bot.event
async def on_guild_channel_create(channel):
    if not bot_can_view(channel):
        await log(f"A channel the bot cannot view was created (id {channel.id})")
        return
    await log(f"#{channel.name} was created")

@bot.event
async def on_guild_channel_update(before, after):
    was_visible, is_visible = bot_can_view(before), bot_can_view(after)
    if was_visible and not is_visible:
        await log(f"The bot can no longer view #{before.name} (id {after.id})")
    elif is_visible and not was_visible:
        await log(f"The bot can view #{after.name} again (id {after.id})")
    elif is_visible and before.name != after.name:
        await log(f"#{before.name} was renamed to #{after.name}")

The "can view again" branch is the documented case. The "can no longer view" branch only fires if Discord sends an update at the moment access is lost, which the docs do not promise. For log structure in general, see our guide to Discord bot error handling and logging.

Backup and audit bots

  • Skip flagged or non-viewable channels when you copy settings, and record their id, type, position and parent_id with a "not visible" marker so the backup still shows where they sat.
  • Never restore a channel from obfuscated data. You would get a channel named ___hidden___ with only an @everyone deny, and the real role overwrites would be gone.
  • Keep your last complete backup. A backup taken before 16 November still holds the real data for private channels, so do not let rotation replace it with an incomplete one.
  • In audit reports, print "bot cannot view this channel" instead of permissions calculated from the placeholder overwrite.
  • End every backup or audit with a line such as "3 channels were left out because the bot cannot view them", plus how to fix it.

Our Discord server backup guide covers what backup bots save in the first place.

What server owners should check

If you run a server rather than a bot, the question is simple: which bots need to see which private channels?

  1. List the bots that log, back up, audit or manage channels, and the private channels each one should cover: the log channel, ticket categories, staff channels you want in backups.
  2. For each of those channels or categories, open the channel settings, go to Permissions, add the bot's role (or the bot itself) and allow View Channel. Channels synced to their category follow the category's permissions. Channels that are not synced need their own overwrite.
  3. Keep in mind that View Channel in a text channel includes reading its messages. Discord describes it as: "Allows guild members to view a channel, which includes reading messages in text channels and joining voice channels". Only grant it on private channels to bots you trust with that content. If you would rather keep a bot out, accept that its backups and reports will skip that channel from 16 November.
  4. Do not hand out Administrator just to keep a backup bot complete. It works because Administrator bypasses every overwrite, but it also gives the bot every other permission.
  5. After 16 November, compare the bot's output with your channel list. A backup or audit with fewer channels than before, or entries called ___hidden___, means the bot lacks View Channel there.

Library support on 16 October 2026

LibraryWhat it says about obfuscationCheck to use
discord.jsNothing in the release notes up to 14.27.0 (15 July 2026). discord-api-types 0.38.54 added ChannelFlags.ChannelObfuscated and GatewayCapabilityBits.ChannelObfuscationchannel.flags against ChannelFlags.ChannelObfuscated or 1 << 17
discord.pyNothing in the changelog up to 2.7.1channel.permissions_for(guild.me).view_channel
JDA6.7.0 (20 September 2026) "makes JDA aware of Channel Obfuscation" and is marked as a breaking change. In interactions you get a detached snapshot of the channel instead of the obfuscated cached onechannel.isObfuscated(), added in PR 3042
Discord.NetNothing in the changelog up to 3.20.2 (8 October 2026)guild.CurrentUser.GetPermissions(channel).ViewChannel

Hosting it with us

Channel obfuscation is a Discord API change, so it behaves the same wherever your bot runs. If you host your bot with us, the Discord bot hosting plans start at Growth: €0.50 a month for 512 MB RAM, billed €3.00 every 6 months, plus a one-time €0.50 setup fee on the 6 month and 1 year terms. The free Starter plan is out of stock at the moment. Paid plans restart your bot automatically after a crash, and your token goes into environment variables instead of your code. Hosting support covers uptime, the panel and deploys, not your bot's own code, so the changes above are yours to make.

FAQ

When does Discord channel obfuscation become mandatory for bots? On Monday 16 November 2026, for all bots. Discord gives the date but no time. Until then you can turn it on for your own application with the Private Channel Obfuscation toggle in the Developer Portal.

Can a bot opt out of channel obfuscation? Not according to Discord's documentation, which mentions no opt-out and calls the change mandatory for all bots. A bot only gets full data for a channel it can view, so the fix is View Channel on the channels the bot needs.

Why is my Discord bot missing channels? From 16 November, GET /guilds/{guild.id}/channels leaves out every channel the bot lacks VIEW_CHANNEL for, without an error. In discord.js, guild.channels.fetch() uses that endpoint, and so does Guild.fetch_channels() in discord.py. Give the bot View Channel on the channels it needs, or treat the missing ones as hidden rather than deleted.

How do I detect a hidden channel in code? Check the channel's flags for CHANNEL_OBFUSCATED (1 << 17). Discord says not to rely on the placeholder name or any other obfuscated field. The flag only appears on Gateway data, because the HTTP endpoint leaves hidden channels out instead.

Do slash commands still get full channel data? Yes. Discord builds interaction payloads "through a separate path that does not apply obfuscation", so your app gets full channel data in interactions for channels it can act on.

Related guides: Discord API changes in 2026 | Discord bot intents and the Gateway | Discord server backup solutions | Discord bot error handling and logging