From bbb8e45352b9f7788b0440091dfa9112990a89f4 Mon Sep 17 00:00:00 2001 From: vmphase Date: Tue, 21 Jul 2026 18:29:57 +0300 Subject: [PATCH 1/9] fix(invite): guard against None code --- discord/invite.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/discord/invite.py b/discord/invite.py index 198f0987bf..f020ea0e08 100644 --- a/discord/invite.py +++ b/discord/invite.py @@ -545,7 +545,7 @@ def __init__( ): self._state: ConnectionState = state self.max_age: int | None = data.get("max_age") - self.code: str = data["code"] + self.code: str | None = data.get("code") self.guild: InviteGuildType | None = self._resolve_guild( data.get("guild"), guild ) @@ -689,7 +689,7 @@ def _resolve_roles( return [Object(role_id) for role_id in role_ids] def __str__(self) -> str: - return self.url + return self.code or "" def __repr__(self) -> str: return ( @@ -712,6 +712,8 @@ def id(self) -> str: @property def url(self) -> str: """A property that retrieves the invite URL.""" + if self.code is None: + raise ValueError("Cannot build an invite URL when code is None") return f"{self.BASE}/{self.code}{f'?event={self.scheduled_event.id}' if self.scheduled_event else ''}" @property From 2bd7aceeacbd455af685137a375e328c68b9e08b Mon Sep 17 00:00:00 2001 From: vmphase Date: Tue, 21 Jul 2026 18:33:02 +0300 Subject: [PATCH 2/9] chore: update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7647529eee..0892805efb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ These changes are available on the `master` branch, but have not yet been releas ### Fixed +- Fix `Invite.url` producing `"https://discord.gg/None"` when `code` is `None` + (from `VanityInvitePayload`). Now raises `ValueError` on `.url` and returns`""` + from `__str__`. + ([#3313](https://github.com/Pycord-Development/pycord/pull/3313)) - Fix an attribute error in `RoleColours.is_holographic()` when `secondary` or `tertiary` is `None`. ([#3268](https://github.com/Pycord-Development/pycord/pull/3268)) From 43af42c129cb94678bac71042492b0a5cd8b5cef Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 15:33:38 +0000 Subject: [PATCH 3/9] style(pre-commit): auto fixes from pre-commit.com hooks --- CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0892805efb..fb7be1ad27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,10 +20,9 @@ These changes are available on the `master` branch, but have not yet been releas ### Fixed -- Fix `Invite.url` producing `"https://discord.gg/None"` when `code` is `None` - (from `VanityInvitePayload`). Now raises `ValueError` on `.url` and returns`""` - from `__str__`. - ([#3313](https://github.com/Pycord-Development/pycord/pull/3313)) +- Fix `Invite.url` producing `"https://discord.gg/None"` when `code` is `None` (from + `VanityInvitePayload`). Now raises `ValueError` on `.url` and returns`""` from + `__str__`. ([#3313](https://github.com/Pycord-Development/pycord/pull/3313)) - Fix an attribute error in `RoleColours.is_holographic()` when `secondary` or `tertiary` is `None`. ([#3268](https://github.com/Pycord-Development/pycord/pull/3268)) From ed05041a7f683323fcf6654343cf30c03901e8a9 Mon Sep 17 00:00:00 2001 From: vmphase Date: Tue, 21 Jul 2026 18:38:40 +0300 Subject: [PATCH 4/9] fix(invite): return "" on id property when code is None --- discord/invite.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/invite.py b/discord/invite.py index f020ea0e08..de31f12bcf 100644 --- a/discord/invite.py +++ b/discord/invite.py @@ -705,9 +705,9 @@ def __hash__(self) -> int: return hash(self.code) @property - def id(self) -> str: + def id(self) -> str: # type: ignore[override] """Returns the proper code portion of the invite.""" - return self.code + return self.code or "" @property def url(self) -> str: From d348ffcd27358a20e0a5d995f7504b164872b25a Mon Sep 17 00:00:00 2001 From: vmphase Date: Tue, 21 Jul 2026 18:46:34 +0300 Subject: [PATCH 5/9] refactor(types/invite): do not inherit VanityInvite from _InviteMetadata --- discord/types/invite.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/discord/types/invite.py b/discord/types/invite.py index 584b573094..a7ed6a2fe7 100644 --- a/discord/types/invite.py +++ b/discord/types/invite.py @@ -49,8 +49,9 @@ class _InviteMetadata(TypedDict, total=False): expires_at: str | None -class VanityInvite(_InviteMetadata): +class VanityInvite(TypedDict): code: str | None + uses: int class IncompleteInvite(_InviteMetadata): From 70099ecac285f20b84cb2642c0a21ec61114ba1d Mon Sep 17 00:00:00 2001 From: vmphase Date: Tue, 21 Jul 2026 18:52:23 +0300 Subject: [PATCH 6/9] fix(invite): raise ValueError when code is None --- discord/invite.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/discord/invite.py b/discord/invite.py index de31f12bcf..f0737dc145 100644 --- a/discord/invite.py +++ b/discord/invite.py @@ -721,7 +721,14 @@ def target_users(self) -> InviteTargetUsers: """An :class:`InviteTargetUsers` object for managing the target users list for this invite. .. versionadded:: 2.8 + + Raises + ------ + ValueError + The invite has no code. """ + if self.code is None: + raise ValueError("Cannot manage target users for an invite with no code") return InviteTargetUsers(invite_code=self.code, state=self._state) async def edit_target_users(self, target_users_file: File) -> None: @@ -740,6 +747,8 @@ async def edit_target_users(self, target_users_file: File) -> None: Raises ------ + ValueError + The invite has no code. HTTPException Updating the target users failed. Forbidden @@ -747,6 +756,8 @@ async def edit_target_users(self, target_users_file: File) -> None: NotFound The invite is invalid or expired. """ + if self.code is None: + raise ValueError("Cannot edit target users for an invite with no code") await self._state.http.update_invite_target_users( self.code, target_users_file=target_users_file ) @@ -766,6 +777,8 @@ async def fetch_target_users_job_status(self) -> InviteTargetUsersJobStatus: Raises ------ + ValueError + The invite has no code. HTTPException Fetching the job status failed. NotFound @@ -773,6 +786,10 @@ async def fetch_target_users_job_status(self) -> InviteTargetUsersJobStatus: Forbidden You do not have permission to view the target users. """ + if self.code is None: + raise ValueError( + "Cannot fetch target users job status for an invite with no code" + ) r = await self._state.http.get_invite_target_users_job_status(self.code) return InviteTargetUsersJobStatus(data=r) @@ -790,6 +807,8 @@ async def delete(self, *, reason: str | None = None): Raises ------ + ValueError + The invite has no code. Forbidden You do not have permissions to revoke invites. NotFound @@ -798,6 +817,8 @@ async def delete(self, *, reason: str | None = None): Revoking the invite failed. """ + if self.code is None: + raise ValueError("Cannot delete an invite with no code") await self._state.http.delete_invite(self.code, reason=reason) def set_scheduled_event(self, event: ScheduledEvent) -> None: From 6a5cfa1965e76b21854622cb5dcbecf9166a8a38 Mon Sep 17 00:00:00 2001 From: vmphase Date: Tue, 21 Jul 2026 18:56:39 +0300 Subject: [PATCH 7/9] refactor(types/invite): inline _InviteMetadata into IncompleteInvite --- discord/types/invite.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/discord/types/invite.py b/discord/types/invite.py index a7ed6a2fe7..6d2b511b69 100644 --- a/discord/types/invite.py +++ b/discord/types/invite.py @@ -40,23 +40,20 @@ InviteTargetType = Literal[1, 2] -class _InviteMetadata(TypedDict, total=False): - uses: int - max_uses: int - max_age: int - temporary: bool - created_at: str - expires_at: str | None - - class VanityInvite(TypedDict): code: str | None uses: int -class IncompleteInvite(_InviteMetadata): +class IncompleteInvite(TypedDict): code: str channel: PartialChannel + uses: NotRequired[int] + max_uses: NotRequired[int] + max_age: NotRequired[int] + temporary: NotRequired[bool] + created_at: NotRequired[str] + expires_at: NotRequired[str | None] class Invite(IncompleteInvite): From 1812204b26a00a9bd59f87b34ba9c4a134ad7671 Mon Sep 17 00:00:00 2001 From: vmphase Date: Tue, 21 Jul 2026 18:58:27 +0300 Subject: [PATCH 8/9] chore: update changelog --- CHANGELOG.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb7be1ad27..16603379cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,11 +18,16 @@ These changes are available on the `master` branch, but have not yet been releas ### Changed +- Inline `_InviteMetadata` fields into `IncompleteInvite` and remove the now-unused + `_InviteMetadata` TypedDict. + ([#3313](https://github.com/Pycord-Development/pycord/pull/3313)) + ### Fixed -- Fix `Invite.url` producing `"https://discord.gg/None"` when `code` is `None` (from - `VanityInvitePayload`). Now raises `ValueError` on `.url` and returns`""` from - `__str__`. ([#3313](https://github.com/Pycord-Development/pycord/pull/3313)) +- Fix `Invite.code` handling when `None` (from `VanityInvitePayload`). `__str__` + falls back to `""`, `.url` and all code-dependent methods raise `ValueError`, + and `Invite.code` is typed as `str | None`. + ([#3313](https://github.com/Pycord-Development/pycord/pull/3313)) - Fix an attribute error in `RoleColours.is_holographic()` when `secondary` or `tertiary` is `None`. ([#3268](https://github.com/Pycord-Development/pycord/pull/3268)) From bc000829d1edbef84d10570b44f06cad5faf26ad Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 15:59:00 +0000 Subject: [PATCH 9/9] style(pre-commit): auto fixes from pre-commit.com hooks --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16603379cf..f461265343 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,9 +24,9 @@ These changes are available on the `master` branch, but have not yet been releas ### Fixed -- Fix `Invite.code` handling when `None` (from `VanityInvitePayload`). `__str__` - falls back to `""`, `.url` and all code-dependent methods raise `ValueError`, - and `Invite.code` is typed as `str | None`. +- Fix `Invite.code` handling when `None` (from `VanityInvitePayload`). `__str__` falls + back to `""`, `.url` and all code-dependent methods raise `ValueError`, and + `Invite.code` is typed as `str | None`. ([#3313](https://github.com/Pycord-Development/pycord/pull/3313)) - Fix an attribute error in `RoleColours.is_holographic()` when `secondary` or `tertiary` is `None`.